Closed thierrydoc closed 4 years ago
Hi, could you please share your ESLint config and Actions workflow?
Sure
Here is the eslint.rc
module.exports = { "env": { "browser": true }, "extends": [ "plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/recommended-requiring-type-checking" ], "parser": "@typescript-eslint/parser", "parserOptions": { "project": "tsconfig.json", "sourceType": "module" }, "plugins": [ "import", "prefer-arrow", "@typescript-eslint", "@typescript-eslint/tslint" ], "rules": { "@typescript-eslint/array-type": "error", "@typescript-eslint/consistent-type-definitions": "error", "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-member-accessibility": [ "error", { "accessibility": "explicit" } ], "@typescript-eslint/indent": [ "off", 2, { "FunctionDeclaration": { "parameters": "first" }, "FunctionExpression": { "parameters": "first" } } ], "@typescript-eslint/interface-name-prefix": "off", "@typescript-eslint/member-delimiter-style": [ "error", { "multiline": { "delimiter": "semi", "requireLast": true }, "singleline": { "delimiter": "semi", "requireLast": false } } ], "@typescript-eslint/member-ordering": "error", "@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-parameter-properties": "off", "@typescript-eslint/no-use-before-define": "off", "@typescript-eslint/no-unused-vars": "off", "@typescript-eslint/prefer-for-of": "error", "@typescript-eslint/prefer-function-type": "error", "@typescript-eslint/quotes": [ "error", "single" ], "@typescript-eslint/semi": [ "error", "always" ], "@typescript-eslint/unbound-method": "off", "@typescript-eslint/unified-signatures": "error", "arrow-body-style": "error", "arrow-parens": [ "off", "as-needed" ], "camelcase": "error", "comma-dangle": [ "error", "always-multiline" ], "complexity": "off", "constructor-super": "error", "curly": "error", "dot-notation": "error", "eol-last": "error", "eqeqeq": [ "error", "smart" ], "guard-for-in": "error", "id-blacklist": [ "error", "any", "Number", "number", "String", "string", "Boolean", "boolean", "Undefined", "undefined" ], "id-match": "error", "import/order": "error", "max-classes-per-file": [ "error", 1 ], "max-len": [ "error", { "code": 120 } ], "new-parens": "error", "no-bitwise": "error", "no-caller": "error", "no-cond-assign": "error", "no-console": "off", "no-debugger": "error", "no-empty": "error", "no-eval": "error", "no-fallthrough": "off", "no-invalid-this": "off", "no-multiple-empty-lines": "error", "no-new-wrappers": "error", "no-shadow": [ "error", { "hoist": "all" } ], "no-throw-literal": "error", "no-trailing-spaces": "error", "no-undef-init": "error", "no-underscore-dangle": "error", "no-unsafe-finally": "error", "no-unused-expressions": "error", "no-unused-labels": "error", "object-shorthand": "error", "one-var": [ "error", "never" ], "prefer-arrow/prefer-arrow-functions": "error", "quote-props": [ "error", "consistent-as-needed" ], "radix": "error", "space-before-function-paren": [ "error", { "anonymous": "never", "asyncArrow": "always", "named": "never" } ], "spaced-comment": "error", "use-isnan": "error", "valid-typeof": "off", "@typescript-eslint/tslint/config": [ "error", { "rules": { "import-spacing": true, "jsdoc-format": true, "no-reference-import": true, "object-literal-sort-keys": true, "one-line": [ true, "check-catch", "check-else", "check-finally", "check-open-brace", "check-whitespace" ], "whitespace": [ true, "check-branch", "check-decl", "check-operator", "check-separator", "check-type", "check-typecast" ] } } ] } };
and then the Action workflow
`name: Linters
on: [push]
jobs: linters:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install Node.js dependencies
run: npm install
- name: Run Linters ESLINT
uses: samuelmeuli/lint-action@v1.2.0
with:
github_token: ${{ secrets.github_token }}
# Enable your linters here
eslint: true
eslint_dir: 'source/js'
eslint_extensions: js,ts,tsx
`
Thanks
Is your repo open source? If not, could you please run eslint --ext js,ts,tsx --no-color --format json "."
in source/js
and add the output here?
Also, please format your comments in the future to make the content readable.
The repo is not open source, but i can paste the content here (I try to format correctly this time)
[
{
"filePath":"/pwd/styleguide/source/js/components/ButtonTabsMenu/ButtonTabsMenu-data.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"\n/**\n * Author: D B\n * DateTime: 19/05/22\n * Description: ButtonTabsMenu data\n * Will look for ids in the DOM and initialise the corresponding\n * react elements.\n */\n\nimport * as React from 'react';\nimport { ButtonTabsMenu } from './ButtonTabsMenu';\n\nexport const buttonTabsMenus = [\n {\n reactElement : (\n <ButtonTabsMenu\n tabs={[\n {\n id: 'id1',\n title: 'item1',\n },\n {\n id: 'id2',\n title: 'item2',\n },\n {\n id: 'id3',\n title: 'way longer item3',\n },\n {\n id: 'id4',\n title: 'i4',\n },\n ]}\n selected=\"id3\"\n onChange={(e, id) => console.log(id + ' has been selected.')}\n />\n ),\n rootId: 'button-tabs-menu',\n },\n];\n"
},
{
"filePath":"/pwd/styleguide/source/js/components/ButtonTabsMenu/ButtonTabsMenu.test.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"/**\n * Author: D B\n * DateTime: 19/05/21\n * Description: Tests for ButtonTabsMenu\n */\n\nimport { mount, render, shallow } from 'enzyme';\nimport * as React from 'react';\nimport {\n getComponents,\n simpleSnapshots,\n} from '../../dsg-test-util';\nimport { ButtonTabsMenu } from './ButtonTabsMenu';\nimport { buttonTabsMenus } from './ButtonTabsMenu-data';\n\nconst components = getComponents(buttonTabsMenus, [\n 'button-tabs-menu',\n]);\n\nsimpleSnapshots(components);\n\n// Test toggle open and toggle close\ntest('Select/Deselect elements', () => {\n const component = shallow(components[0].reactElement);\n const instance: ButtonTabsMenu = component.instance() as ButtonTabsMenu;\n expect(instance.state.selected).toEqual('id3');\n\n // Simulate selection of first tab\n instance.selectTab(new CustomEvent('click'), 'id1', () => {\n expect(instance.state.selected).toEqual('id1');\n expect(component).toMatchSnapshot();\n\n // Simulate selection of second index\n instance.selectTab(new CustomEvent('click'), 'id2', () => {\n expect(instance.state.selected).toEqual('id2');\n expect(component).toMatchSnapshot();\n });\n });\n});\n"
},
{
"filePath":"/pwd/styleguide/source/js/components/ButtonTabsMenu/ButtonTabsMenu.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"\n/**\n * Author: D B\n * DateTime: 19/05/22\n * Description: ButtonTabsMenu React component, elegant menu system that looks like\n * its items are buttons.\n */\n\nimport * as React from 'react';\nimport {\n findInArray,\n setCSSClasses,\n} from '../../dsg-util';\n\n// Interface for single Tab\nexport interface ButtonTab {\n title: string;\n id: string;\n}\n\nexport interface ButtonTabsMenuProps {\n /** Array of Tabs. A tab is simply a title and an ID. */\n tabs: ButtonTab[];\n /** Id of selected tab */\n selected?: string;\n /** Event handler triggered after selection of a new tab */\n onChange?: (e: Event, selected: string) => void;\n}\n\nexport interface ButtonTabsMenuState {\n /** Id of selected tab */\n selected: string;\n /** `selected` Prop stored in a state cache */\n cachedSelected: string;\n}\n\nexport class ButtonTabsMenu extends React.Component<\n ButtonTabsMenuProps,\n ButtonTabsMenuState\n> {\n\n public static defaultProps: Partial<ButtonTabsMenuProps> = {\n selected: null,\n tabs: [],\n };\n\n constructor(props: ButtonTabsMenuProps) {\n super(props);\n\n this.state = {\n cachedSelected: props.selected,\n selected: props.selected,\n };\n }\n\n // --------------------------------- RENDER -------------------------------\n\n public render() {\n return (\n <menu role=\"radiogroup\">\n {this.props.tabs.map((tab, i) => this.displayTab(tab, i))}\n </menu>\n );\n }\n\n /**\n * Display individual tab\n *\n * @param tab: ButtonTab\n * @param i: number\n * @return ReactNode\n */\n public displayTab(tab: ButtonTab, i: number) {\n const isSelected = this.state.selected === tab.id;\n\n const tabClasses = setCSSClasses({\n 'active': isSelected,\n 'dsg-Button-tab': true,\n });\n\n return (\n <button\n role=\"radio\"\n aria-checked={isSelected}\n className={tabClasses}\n onClick={e => this.selectTab(e, tab.id)}\n key={'button-tab-' + tab.id}>\n {this.displaySelectOverlay(tab.id, i)}\n <div className=\"dsg-Button-tab__fake-border\"></div>\n <span className=\"dsg-Button-tab__title\">\n {tab.title}\n </span>\n </button>\n );\n }\n\n /**\n * Display animated overlay that travels between each tab\n * there is one per tab, which becomes invisible when it's overflowing\n *\n * @param targetId: string\n * @param i: number\n * @return ReactNode\n */\n public displaySelectOverlay(targetId: string, i: number) {\n\n // Retrieve index of the selected tab\n const selectedTabIndex = findInArray(this.props.tabs, (val) => val.id === this.state.selected);\n\n // If we cannot find it, no overlay should be displayed\n if (selectedTabIndex === -1) {\n return null;\n }\n\n // We'll try to place the overlay the closest to the active tab\n const overlayPosition = ((selectedTabIndex + 1) - (i + 1)) * 100;\n const overlayStyle = {\n left: 'calc(' + overlayPosition + '% + 10px)',\n } as React.CSSProperties;\n\n return (\n <div style={overlayStyle} className=\"dsg-Button-tab__overlay\"></div>\n );\n }\n\n // --------------------------------- COMPONENT LIFECYCLE -------------------------------\n\n /**\n * Allow the `selected` state to be controlled from the parent component\n */\n // tslint:disable-next-line:member-ordering\n public static getDerivedStateFromProps(props: ButtonTabsMenuProps, state: ButtonTabsMenuState) {\n let {selected} = state;\n // Compare if selected from props has changed\n if (state.cachedSelected !== props.selected) {\n // We want to allow selection change from prop change\n selected = props.selected;\n }\n\n return {\n cachedSelected: props.selected,\n selected,\n };\n }\n\n // --------------------------------- CUSTOM FUNCTIONS -------------------------------\n\n /**\n * Change selected tab\n *\n * @param e: Event\n * @param id : string\n * @param callback : Function\n */\n public selectTab(e, id: string, callback?: () => void) {\n // Call optional prop handler\n if (this.props.onChange) {\n this.props.onChange(e, id);\n }\n\n this.setState({selected: id}, callback);\n }\n}\n"
},
{
"filePath":"/pwd/styleguide/source/js/components/Calendar/Calendar-data.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"/**\n * Author: D B\n * DateTime: 18/10/03\n * LastUpdated: 18/10/15\n * Description: Example datas for calendar\n */\n\nimport * as moment from 'moment-timezone';\nimport * as React from 'react';\nimport slots from '../../datas/slots';\nimport { Calendar } from './Calendar';\n\nconst onDayChange = (newDate: moment.Moment) => {\n console.log('Calendar has changed date : ', newDate.format('YYYY/MM/DD'));\n};\n\nconst onSlotPick = (e, slot) => {\n console.log('A slot has been picked : ', moment.unix(slot.start).format('YYYY/MM/DD HH:mm'));\n};\n\nconst onDuplicateSlotPick = (e, duplicates) => {\n console.log('A slot with duplicates has been picked : ', duplicates);\n};\n\nconst onPrevious = (e, newDate: moment.Moment) => {\n console.log('The user clicked on the previous arrow : ', newDate.format('YYYY/MM/DD'));\n};\n\nconst onNext = (e, newDate: moment.Moment) => {\n console.log('The user clicked on the next arrow : ', newDate.format('YYYY/MM/DD'));\n};\n\nconst onDatepickerChange = (e, newDate: moment.Moment) => {\n console.log('The user picked a date on the datepicker : ', newDate.format('YYYY/MM/DD'));\n};\n\nexport const calendars = [\n {\n reactElement : (\n <Calendar\n locale=\"en\"\n timezone=\"Europe/Luxembourg\"\n firstDay={moment('2099-12-24').unix()} // Pick distant date to not be annoyed by past\n slots={slots}\n nbSlotsPreview={5}\n // Fixed minDay to avoid tests snapshot to change every day...\n minDay={moment('2099-12-10').unix()}\n // showDuplicates\n // dateFormat='YYYY'\n onDayChange={onDayChange}\n onSlotPick={onSlotPick}\n onDuplicateSlotPick={onDuplicateSlotPick}\n onPrevious={onPrevious}\n onNext={onNext}\n onDatepickerChange={onDatepickerChange}\n // textNextSlot={(<span>NEXT</span>)}\n />\n ),\n rootId: 'calendar-default',\n },\n\n // EMPTY CALENDAR\n {\n reactElement: (\n <Calendar\n firstDay={moment('2100-01-01').unix()} />\n ),\n rootId: 'calendar-empty',\n },\n\n // LOADING CALENDAR (and empty)\n {\n reactElement: (\n <Calendar\n firstDay={moment('2100-01-01').unix()}\n loading />\n ),\n rootId: 'calendar-loading',\n },\n];\n"
},
{
"filePath":"/pwd/styleguide/source/js/components/Calendar/Calendar.test.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"\n/**\n * Author: D B\n * DateTime: 18/10/16\n * LastUpdated: 18/10/16\n * Description: Tests for Calendar react component\n */\n\nimport { mount, render, shallow } from 'enzyme';\nimport { jsdom } from 'jsdom';\nimport * as moment from 'moment-timezone';\nimport * as React from 'react';\nimport {\n getComponents,\n simpleSnapshots,\n} from '../../dsg-test-util';\nimport { Calendar } from './Calendar';\nimport { calendars } from './Calendar-data';\n\nconst components = getComponents(calendars, [\n 'calendar-default',\n 'calendar-empty',\n 'calendar-loading',\n]);\n\nsimpleSnapshots(components);\n\n// Test toggle preview of Calendar\ntest('test Toggle preview mode', () => {\n const component = shallow(components[0].reactElement);\n const instance: Calendar = component.instance() as Calendar;\n expect(instance.state.isPreview).toBeTruthy();\n\n // Simulate Preview off\n instance.togglePreviewMode(new CustomEvent('click'), () => {\n expect(instance.state.isPreview).toBeFalsy();\n expect(component).toMatchSnapshot();\n\n // Simulate Preview back on\n instance.togglePreviewMode(new CustomEvent('click'), () => {\n expect(instance.state.isPreview).toBeTruthy();\n expect(component).toMatchSnapshot();\n });\n });\n});\n\n// Test toggle react-datepicker\ntest('test Toggle datepicker', () => {\n const component = shallow(components[0].reactElement);\n const instance: Calendar = component.instance() as Calendar;\n expect(instance.state.datepickerToggled).toBeFalsy();\n\n // Simulate Preview off\n instance.toggleDatepicker(new CustomEvent('click'), () => {\n expect(instance.state.datepickerToggled).toBeTruthy();\n expect(component).toMatchSnapshot();\n\n // Simulate Preview back on\n instance.toggleDatepicker(new CustomEvent('click'), () => {\n expect(instance.state.datepickerToggled).toBeFalsy();\n expect(component).toMatchSnapshot();\n });\n });\n});\n\n// Test simulated effects of a resize window\ntest('Test mobile screen.', () => {\n const component = shallow(components[0].reactElement);\n const instance: Calendar = component.instance() as Calendar;\n\n instance.setState({\n isMobile: false,\n nbDays: 3,\n }, () => {\n expect(component).toMatchSnapshot();\n });\n});\n\n// Simulate and test click on next button\ntest('test navigation next', () => {\n const component = shallow(components[0].reactElement);\n const instance: Calendar = component.instance() as Calendar;\n\n instance.handleClickOnNext(new CustomEvent('click'), () => {\n expect(component).toMatchSnapshot();\n });\n});\n\n// Simulate and test click on previous button\ntest('test navigation previous', () => {\n const component = shallow(components[0].reactElement);\n const instance: Calendar = component.instance() as Calendar;\n\n instance.handleClickOnPrevious(new CustomEvent('click'), () => {\n expect(component).toMatchSnapshot();\n });\n});\n\n// Simulate and test click on previous button\ntest('test navigation datepicker', () => {\n const component = shallow(components[0].reactElement);\n const instance: Calendar = component.instance() as Calendar;\n\n instance.handleDatepickerChange(moment('2200-01-01'), new CustomEvent('click'), () => {\n expect(component).toMatchSnapshot();\n });\n});\n\n// Simulate and test click on previous button\ntest('test navigation no slots', () => {\n const component = shallow(components[0].reactElement);\n const instance: Calendar = component.instance() as Calendar;\n\n // Go to view with no slots\n instance.handleClickOnPrevious(new CustomEvent('click'), () => {\n\n // Simulate click on no slot link (redirect to associated slot)\n instance.handleClickOnNoSlotLink(new CustomEvent('click'), moment('2099-12-24'), () => {\n expect(component).toMatchSnapshot();\n });\n });\n});\n\n// Simulate left over functions that doesn't change state or calendar\ntest('test miscellaneous', () => {\n const component = shallow(components[0].reactElement);\n const instance: Calendar = component.instance() as Calendar;\n\n // Simulate click on normal slot\n instance.handleClickOnSlot(\n new CustomEvent('click'),\n {start: moment('2100-12-26').unix()},\n );\n\n // Simulate click on duplicate slot\n instance.handleClickOnSlot(\n new CustomEvent('click'),\n {\n hasDuplicate: true,\n start: 4101865200,\n },\n );\n});\n"
},
{
"filePath":"/pwd/styleguide/source/js/components/Calendar/Calendar.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"\n/**\n * Author: D B\n * DateTime: 18/10/03\n * LastUpdated: 18/10/16\n * Description: React component that displays slots of time inside a calendar\n */\n\nimport * as moment from 'moment-timezone';\nimport * as React from 'react';\nimport {\n debounce,\n findInArray,\n isEmpty,\n isEmptyObj,\n isSmallScreen,\n setCSSClasses,\n times,\n} from '../../dsg-util';\n\nimport DatePicker from 'react-datepicker';\nimport Slot from '../../models/Slot';\n\nexport interface CalendarProps {\n /** Language ISO for setting language of moment library i.e 'fr', 'de', 'en', ... */\n locale?: string;\n /**\n * Timezone to use for calendar i.e 'Europe/Luxembourg', 'Europe/Lisbon', ...\n * By default it uses the user browser's timezone.\n */\n timezone?: string;\n /**\n * unix timestamp (without milliseconds) : Set with which day should the calendar\n * start displaying slots. By default it's today's date\n */\n firstDay?: number;\n /**\n * What's the maximum number of days that should be displayed if space allows it.\n * Each day displayed will need at least a width of 70px.\n */\n maxNbDays?: number;\n /**\n * Unix timestamp : The user won't be able to navigate before that date.\n * By default, it's today's date\n */\n minDay?: number;\n /** Info about slots. It's an object, where each key is a unix timestamp of a day,\n * and their values are arrays of Slot that will be displayed in this day.\n * More info about slots objects in the documentation below.\n */\n slots?: { [name: number]: Slot[]};\n /** If false, duplicate slots will be hidden */\n showDuplicates?: boolean;\n /**\n * Number maximum of slots shown on preview. The rest will be hidden until a\n * click on 'show more' happens. This value will also have an impact on the minimum\n * height of the calendar, even if there are no slots. Count a raise of the calendar's\n * height of 35px per slot.\n */\n nbSlotsPreview?: number;\n /**\n * Date format to be used in various across the calendar.\n * Please use the a pattern as described in the\n * [moment documentation](https://momentjs.com/docs/#/parsing/string-format/)\n */\n dateFormat?: string;\n\n /** Event handler triggered after a click on a slot. */\n onSlotPick?: (e: Event, slot: Slot) => void;\n /** Event handler triggered after a click on a slot that has duplicates. */\n onDuplicateSlotPick?: (e: Event, duplicates: Slot[]) => void;\n /** Event handler triggered after a click on the previous arrow but before changing time. */\n onPrevious?: (e: Event, newFirstDay: moment.Moment) => void;\n /** Event handler triggered after a click on the next arrow but before changing time. */\n onNext?: (e: Event, newFirstDay: moment.Moment) => void;\n /** Event handler triggered after the days have been changed (after render). */\n onDayChange?: (newFirstDay: moment.Moment) => void;\n /** Event handler triggered after the date has been changed in the datepicker. */\n onDatepickerChange?: (e: Event, newFirstDay: moment.Moment) => void;\n\n /** Loading state. Will hide view and show loader instead */\n loading?: boolean;\n\n /** Text for link that shows more slots and toggle the preview mode off */\n textMore?: string | React.ReactNode;\n /** Text for link that shows less slots and toggle the preview mode on */\n textLess?: string | React.ReactNode;\n /** Text for view with no slots, but when there is a slot available at a further date */\n textNextSlot?: string | React.ReactNode;\n /** Text for view with no slots, but when there is a slot available at a previous date */\n textPreviousSlot?: string | React.ReactNode;\n /** Text for view with no slots */\n textNoSlots?: string | React.ReactNode;\n}\n\nexport interface CalendarState {\n /** Set with which day should the calendar start displaying slots */\n firstDay: moment.Moment;\n /**\n * Same as above as a unix timestamp (without milliseconds),\n * used for comparisons in getDerivedStateFromProp method\n */\n cachedFirstDay: number;\n /** Indicates how many days in a row should be displayed in the calendar */\n nbDays: number;\n /** Indicates if we should show the datepicker or not */\n datepickerToggled: boolean;\n /** Indicates if we should show all slots or just a preview (see prop nbSlotsPreview) */\n isPreview: boolean;\n /** Used for adjusting the view to mobile devices */\n isMobile: boolean;\n}\n\nexport class Calendar extends React.Component<\n CalendarProps,\n CalendarState\n> {\n\n public static defaultProps: Partial<CalendarProps> = {\n dateFormat: 'L',\n firstDay: moment().unix(),\n loading: false,\n locale: 'en',\n maxNbDays: 7,\n minDay: moment().unix(),\n nbSlotsPreview: 5,\n showDuplicates: false,\n slots: {},\n textLess: 'Less',\n textMore: 'More',\n textNextSlot: 'Next date with slots : ',\n textNoSlots: 'There are no slots available at any date.',\n textPreviousSlot: 'There are no slots available at a further date. Last date with slots : ',\n };\n\n private calendarRef: React.RefObject<HTMLDivElement>;\n\n constructor(props: CalendarProps) {\n super(props);\n this.calendarRef = React.createRef();\n\n // Setting moment global locale (language)\n moment.locale(props.locale);\n const firstDay = moment.unix(props.firstDay).locale(props.locale);\n\n // Setting moment global timezone\n if (props.timezone) {\n moment.tz.setDefault(props.timezone);\n firstDay.tz(props.timezone);\n }\n\n this.state = {\n cachedFirstDay: props.firstDay,\n datepickerToggled: false,\n firstDay: firstDay.startOf('day'),\n isMobile: isSmallScreen(),\n isPreview: true,\n nbDays: 7,\n };\n\n // Render methods\n this.displayMonth = this.displayMonth.bind(this);\n this.displayDatepicker = this.displayDatepicker.bind(this);\n this.displayBody = this.displayBody.bind(this);\n this.displayColumnHeader = this.displayColumnHeader.bind(this);\n this.displayColumnSlots = this.displayColumnSlots.bind(this);\n this.displaySlot = this.displaySlot.bind(this);\n this.displayTogglePreview = this.displayTogglePreview.bind(this);\n this.displayNoSlotsView = this.displayNoSlotsView.bind(this);\n\n // Handle methods\n this.handleResize = this.handleResize.bind(this);\n this.handleClickOnSlot = this.handleClickOnSlot.bind(this);\n this.handleClickOnPrevious = this.handleClickOnPrevious.bind(this);\n this.handleClickOnNext = this.handleClickOnNext.bind(this);\n this.handleDatepickerChange = this.handleDatepickerChange.bind(this);\n this.handleClickOnNoSlotLink = this.handleClickOnNoSlotLink.bind(this);\n\n // Manipulate time\n this.toggleDatepicker = this.toggleDatepicker.bind(this);\n this.changeDate = this.changeDate.bind(this);\n this.isPreviousPossible = this.isPreviousPossible.bind(this);\n this.getClosestSlotInfo = this.getClosestSlotInfo.bind(this);\n this.getNumberOfDays = this.getNumberOfDays.bind(this);\n this.sortSlots = this.sortSlots.bind(this);\n this.setSlotsSpace = this.setSlotsSpace.bind(this);\n this.processSlots = this.processSlots.bind(this);\n\n // Others\n this.togglePreviewMode = this.togglePreviewMode.bind(this);\n }\n\n // --------------------------------- RENDER -------------------------------\n\n public render() {\n const calendarClasses = setCSSClasses({\n 'dsg-Calendar': true,\n 'dsg-Calendar--loading': this.props.loading,\n });\n\n return (\n <div\n className={calendarClasses}\n ref={this.calendarRef}>\n\n {/* HEADER */}\n <div className=\"dsg-space-between\">\n\n {/* Previous days */}\n {\n this.isPreviousPossible() ? (\n <a\n className=\"dsg-Calendar__previous\"\n onClick={this.handleClickOnPrevious}><</a>\n ) : <span></span>\n }\n\n {/* Month / DatePicker */}\n <div\n onClick={this.toggleDatepicker}\n className=\"dsg-Calendar__month\">\n {this.displayMonth()}\n </div>\n\n {/* Next days */}\n <a\n className=\"dsg-Calendar__next dsg-vhalign\"\n onClick={this.handleClickOnNext}>></a>\n\n </div>\n\n {/* DATEPICKER */}\n <div className=\"dsg-Calendar__datepicker dsg-abs-halign\">\n {this.displayDatepicker()}\n </div>\n\n {/* BODY */}\n {this.displayBody()}\n\n {/* Loader layer */}\n <div className=\"dsg-Calendar__loader-wrapper dsg-overlay\">\n <div className=\"dsg-loader-ring dsg-loader-ring--dark\"><div></div><div></div><div></div><div></div></div>\n </div>\n </div>\n );\n }\n\n /**\n * Display month string: Can be longer if we are in between two months\n */\n public displayMonth() {\n const {firstDay, nbDays} = this.state;\n const lastDay = moment(firstDay).add(nbDays - 1, 'd');\n let monthLabel = firstDay.format('MMMM YYYY');\n\n /*\n If month of first and last day of calendar are not the same\n we add the next month's name and year to the string\n */\n if (firstDay.format('M') !== lastDay.format('M')) {\n monthLabel += ' - ' + lastDay.format('MMMM YYYY');\n }\n\n return monthLabel;\n }\n\n /**\n * Display datepicker if it has been toggled\n */\n public displayDatepicker() {\n if (!this.state.datepickerToggled) {\n return null;\n }\n\n return(\n <DatePicker\n selected={this.state.firstDay}\n onChange={this.handleDatepickerChange}\n minDate={moment.unix(this.props.minDay).startOf('d')}\n inline />\n );\n }\n\n /**\n * Display body part of calendar (and precompute slots datas)\n */\n public displayBody() {\n\n /*\n We want to refine and compute slots data before sending it\n to the all the display functions affected by it.\n */\n const slots = this.processSlots(this.props.slots);\n\n // Collecting an array of moment (days displayed in the view)\n const theseDays = times(this.state.nbDays, index => {\n return moment(this.state.firstDay).add(index, 'days');\n });\n\n // This variable will be used to display the right view if there are no slots available\n let noSlots = true;\n\n // Checking if one of days in the view\n for (const day of theseDays) {\n if (!isEmpty(slots[day.unix()])) {\n noSlots = false;\n break;\n }\n }\n\n const nbSlots = this.props.nbSlotsPreview;\n const wrapperSlotsStyle = {\n minHeight: (nbSlots ? nbSlots * 38 : 175) + 'px',\n };\n\n // ********** VIEW START HERE *************\n return(\n <div>\n\n {/* Day headers */}\n <div className=\"dsg-Calendar__day-headers dsg-space-between\">\n { theseDays.map(this.displayColumnHeader) }\n </div>\n\n {/* Slots or message */}\n <div\n style={wrapperSlotsStyle}\n className=\"dsg-Calendar__slots-wrapper dsg-space-between\">\n {\n noSlots ?\n this.displayNoSlotsView(slots) :\n theseDays.map(day => this.displayColumnSlots(day, slots))\n }\n </div>\n\n {/* Toggle preview link (show more or less) */}\n {noSlots ? null : this.displayTogglePreview(slots)}\n\n </div>\n );\n }\n\n /**\n * Display column header containing number of day and name of month\n *\n * @param day\n */\n public displayColumnHeader(day) {\n return(\n <div\n className=\"dsg-Calendar__day-header\"\n key={'day-header' + day.unix()}>\n\n {/* Number of day */}\n <div className=\"dsg-Calendar__day-header-number\">\n {day.format('DD')}\n </div>\n\n {/* Name of month */}\n <div className=\"dsg-Calendar__day-header-name\">\n {day.format('ddd')}\n </div>\n\n </div>\n );\n }\n\n /**\n * Display all the slots of a day inside a column\n *\n * @param day\n * @param slots\n */\n public displayColumnSlots(day: moment.Moment, slots) {\n // Retrieve slots of corresponding day\n const daySlots = slots[day.unix()];\n if (!daySlots || isEmpty(daySlots)) {\n return (\n <div\n key={'slots' + day.unix()}\n className=\"dsg-Calendar__slots\"></div>\n );\n }\n\n return (\n <div\n key={'slots' + day.unix()}\n className=\"dsg-Calendar__slots\">\n {daySlots.map((slot, index) => this.displaySlot(slot, index, day))}\n </div>\n );\n }\n\n /**\n * Display a slot and inject attached CSS classes\n *\n * @param slot\n * @param index\n * @param day\n */\n public displaySlot(slot: Slot, index: number, day: moment.Moment) {\n const start = moment.unix(slot.start);\n const end = slot.end ? moment.unix(slot.end) : undefined;\n\n // Throw errors if the slots don't belong to the days they've been attached to\n if (!start.isSame(day, 'day')) {\n console.error(\n 'Calendar error : slot with start time ' + slot.start +\n ' should be on the same day as date ' + day.unix(),\n );\n }\n if (end && !end.isSame(day, 'day')) {\n console.error(\n 'Calendar error : slot with end time ' + slot.end +\n ' should be on the same day as date ' + day.unix(),\n );\n }\n\n // Style slot\n const slotClasses = {\n 'dsg-Slot': true,\n 'dsg-Slot--long': end,\n 'dsg-btn': true,\n 'dsg-btn--success': isEmpty(slot.classNames), // Only applies if not custom styled\n /*\n We hide every slot that exceeds the limited space of the preview mode\n (if it's toggled)\n */\n 'dsg-hide': slot.space > this.props.nbSlotsPreview && this.state.isPreview,\n 'dsg-tooltip': slot.legend,\n 'dsg-tooltip--n': slot.legend,\n };\n\n // Apply custom classes to the slot\n (slot.classNames || []).forEach(className => {\n slotClasses[className] = true;\n });\n\n return(\n <button\n key={'slot-' + slot.start + '-' + index}\n className={setCSSClasses(slotClasses)}\n data-dsg-tooltip={slot.legend ? slot.legend : ''}\n onClick={e => this.handleClickOnSlot(e, slot)}>\n\n {/* Legend */}\n { slot.legend ? (\n <div className=\"dsg-Slot__legend\">\n {slot.legend}\n </div>\n ) : null\n }\n\n {/* Start time */}\n <div>\n {start.format('HH:mm')}\n </div>\n\n {/* End time for long slots */}\n {\n end ? (\n <div>\n <div className=\"dsg-Slot__dash\">-</div>\n <div>\n {end.format('HH:mm')}\n </div>\n </div>\n ) : null\n }\n </button>\n );\n }\n\n /**\n * Display link that allow to toggle from preview mode to\n *\n * @param slots\n */\n public displayTogglePreview(slots: { [name: number]: Slot[] }) {\n\n // No need to show anything if slots are not limited\n const emptyNode = <div className=\"dsg-Calendar__more\"></div>;\n if (!this.props.nbSlotsPreview) {\n return emptyNode;\n }\n\n let showLink = false;\n const firstDay = moment(this.state.firstDay);\n const lastDay = moment(firstDay).add(this.state.nbDays - 1, 'd');\n\n /*\n We browse each day to see if one of them contains enough slots to\n exceeds the preview's limit\n */\n for (const dayKey in slots) {\n if (slots.hasOwnProperty(dayKey)) {\n // We only treat days that are in the range of the view\n const day = moment.unix(parseInt(dayKey, 10));\n if (day.isBefore(firstDay) || day.isAfter(lastDay)) {\n continue;\n }\n\n // Look for a slot that has a space attribute higher than the limit for preview\n const isExceedingPreviewSpace = findInArray(slots[dayKey], slot => {\n return slot.space > this.props.nbSlotsPreview;\n });\n\n if (isExceedingPreviewSpace !== -1) {\n showLink = true;\n break;\n }\n }\n }\n\n if (!showLink) {\n return emptyNode;\n }\n\n const moreText = this.state.isPreview ? this.props.textMore : this.props.textLess;\n\n return(\n <div\n className=\"dsg-Calendar__more\"\n onClick={this.togglePreviewMode}>\n {moreText}\n </div>\n );\n }\n\n /**\n * Display custom message if no slots can be displayed at a particuliar date.\n *\n * @param slots\n */\n public displayNoSlotsView(slots: { [name: number]: Slot[] }) {\n\n const {textNoSlots, closestDayWithSlots, handleClickOnLink} = this.getClosestSlotInfo(slots);\n\n return(\n <div className=\"dsg-Calendar__no-slots dsg-valign\">\n <span className=\"dsg-Calendar__no-slots-text\">\n {textNoSlots}\n\n { closestDayWithSlots ?\n <a href=\"#\"\n onClick={handleClickOnLink}>\n {closestDayWithSlots.format(this.props.dateFormat)}\n </a>\n : '' }\n </span>\n </div>\n );\n }\n\n // --------------------------------- COMPONENT LIFECYCLE -------------------------------\n\n public componentDidMount() {\n\n // Listen to resize event\n window.addEventListener('resize', debounce(this.handleResize, 50));\n\n /*\n Check if nbDays is correct now that the first render is done\n (we need to calculate the natural size of calendar for this)\n */\n const nbDays = this.getNumberOfDays();\n if (nbDays !== this.state.nbDays) {\n this.setState({\n nbDays,\n });\n }\n }\n\n public componentWillUnmount() {\n\n // Remove listener(s)\n window.removeEventListener('resize', debounce(this.handleResize, 50));\n }\n\n // tslint:disable-next-line:member-ordering\n public static getDerivedStateFromProps(props: CalendarProps, state: CalendarState) {\n let firstDay = state.firstDay;\n\n // Compare if firstDay from props has changed\n if (state.cachedFirstDay !== props.firstDay) {\n /*\n If so we set the firstDay to the right locale and timezone\n before setting the hour to midnight.\n */\n firstDay = moment.unix(props.firstDay).locale(props.locale);\n if (props.timezone) {\n firstDay.tz(props.timezone).startOf('d');\n }\n }\n\n return {\n cachedFirstDay: props.firstDay,\n firstDay,\n };\n }\n\n // --------------------------------- CUSTOM FUNCTIONS -------------------------------\n\n /**\n * Handle resize event\n */\n public handleResize() {\n const isMobile = isSmallScreen(); // We should check if we need to toggle Mobile mode\n const nbDays = this.getNumberOfDays(); // We should check how much days still fits in the calendar\n\n if (\n isMobile !== this.state.isMobile ||\n nbDays !== this.state.nbDays\n ) {\n this.setState({nbDays, isMobile});\n }\n }\n\n /**\n * Handle click on slot\n *\n * @param e\n * @param slot\n */\n public handleClickOnSlot(e, slot: Slot) {\n\n // If slot has duplicates, event handler is maybe not the same\n if (slot.hasDuplicate) {\n\n // If a special handler has been set, we want to use this one\n if (this.props.onDuplicateSlotPick) {\n\n // Retrieve duplicates to send to event handler\n const dayStamp = moment.unix(slot.start).startOf('d').unix();\n const daySlots = this.props.slots[dayStamp];\n const duplicates = daySlots.filter(daySlot =>\n daySlot.start === slot.start &&\n daySlot.end === slot.end,\n );\n\n this.props.onDuplicateSlotPick(e, duplicates);\n\n // Otherwise we fall back on onSlotPick event handler\n } else if (this.props.onSlotPick) {\n console.warn('The slot picked has duplicates. Please use onDuplicateSlotPick prop to handle this usecase.');\n this.props.onSlotPick(e, slot);\n\n // No event handlers available\n } else {\n console.warn(\n 'No event handlers has been set on slot pick.' +\n 'Please set onSlotPick and onDuplicateSlotPick props on Calendar component.' +\n 'The slot picked has duplicates.',\n );\n }\n\n // If no duplicates\n } else {\n if (this.props.onSlotPick) {\n this.props.onSlotPick(e, slot);\n } else {\n console.warn('Please set onSlotPick prop on Calendar component.');\n }\n }\n }\n\n /**\n * Handle click on previous arrow - Move chronologically backwards\n */\n public handleClickOnPrevious(e, callback?: () => void) {\n\n const minDay = moment.unix(this.props.minDay).startOf('d');\n const oneWeekBefore = moment(this.state.firstDay).subtract(this.state.nbDays, 'd');\n\n // If going x days backwards leads before our limit, adjust by going back to the limit date\n const firstDay = minDay.isSameOrAfter(oneWeekBefore) ? minDay : oneWeekBefore;\n\n // Event handler for parent component\n if (this.props.onPrevious) {\n this.props.onPrevious(e, firstDay);\n }\n\n // Only change state if some previous days comes after the limit\n if (this.isPreviousPossible()) {\n this.changeDate(firstDay, {}, callback);\n }\n }\n\n /**\n * Handle click on next arrow - Move chronologically forward\n */\n public handleClickOnNext(e, callback?: () => void) {\n const firstDay = moment(this.state.firstDay).add(this.state.nbDays, 'd');\n\n // Event handler for parent component\n if (this.props.onNext) {\n this.props.onNext(e, firstDay);\n }\n\n this.changeDate(firstDay, {}, callback);\n }\n\n /**\n * Function called after a date in the datepicker has been selected\n *\n * @param date\n */\n public handleDatepickerChange(date: moment.Moment, e, callback: () => void) {\n // Event handler for parent component\n if (this.props.onDatepickerChange) {\n this.props.onDatepickerChange(e, date);\n }\n\n const newState = {datepickerToggled: false};\n this.changeDate(date, newState, callback);\n }\n\n /**\n * Method called after a click on a link in the no slot view\n *\n * @param e\n * @param slot\n */\n public handleClickOnNoSlotLink(e, slot: moment.Moment, callback?: () => void) {\n e.preventDefault();\n this.changeDate(slot, {}, callback);\n }\n\n /**\n * Display or hide datepicker\n */\n public toggleDatepicker(e, callback = null) {\n this.setState({\n datepickerToggled: !this.state.datepickerToggled,\n }, callback);\n }\n\n /**\n * Change date and eventually call event handler onDayChange\n *\n * @param firstDay\n * @param newState : Used to pass other state changes\n */\n public changeDate(firstDay: moment.Moment, newState = {}, callback = null) {\n\n // If no custom callback sent (through testing)...\n const realCallback = callback ? callback : (\n // We use the parent component event handler\n this.props.onDayChange ?\n () => this.props.onDayChange(firstDay) :\n null\n );\n\n this.setState(\n // Fuse state changes in one object\n Object.assign(newState, {\n firstDay: firstDay.startOf('d'), // Make sure it's the start of the day\n }),\n realCallback,\n );\n }\n\n /**\n * Returns true if it's possible to go to previous days\n */\n public isPreviousPossible() {\n const minDay = moment.unix(this.props.minDay).startOf('d');\n\n // It should be impossible to go before the limit day\n return this.state.firstDay.isAfter(minDay);\n }\n\n /**\n * Returns info about the closest slot available\n *\n * @param slots : { [name: number]: Slot[] }\n */\n public getClosestSlotInfo(slots: { [name: number]: Slot[] }) {\n const firstDay = moment(this.state.firstDay);\n const lastDay = moment(firstDay).add(this.state.nbDays - 1, 'd');\n let previousDayWithSlots;\n let nextDayWithSlots;\n\n // We will search for the closest days with slots (previous and next)\n for (const day in slots) {\n if (slots.hasOwnProperty(day)) {\n const dayMoment = moment.unix(parseInt(day, 10));\n\n if (\n // Ignore if day doesn't have slots\n isEmpty(slots[day]) ||\n // or if it's before the navigation limit\n dayMoment.isBefore(moment.unix(this.props.minDay))\n ) {\n continue;\n }\n\n /*\n We select this day as the previousDayWithSlots if it's in the past (relatively\n to the first day of the view), and if it's closest to the first day of the view\n than the other previous days with slots.\n */\n if (\n dayMoment.isBefore(firstDay) &&\n (!previousDayWithSlots || dayMoment.isAfter(previousDayWithSlots))\n ) {\n previousDayWithSlots = dayMoment;\n /*\n Same logic is applied here. The nextDayWithSlots will be the closest day in the\n future (with slots) after the last day of the view.\n */\n } else if (\n dayMoment.isAfter(lastDay) &&\n (!nextDayWithSlots || dayMoment.isBefore(nextDayWithSlots))\n ) {\n nextDayWithSlots = dayMoment;\n }\n }\n }\n\n // We want to show in priority a next slot...\n if (nextDayWithSlots || previousDayWithSlots) {\n return {\n closestDayWithSlots: nextDayWithSlots ? nextDayWithSlots : previousDayWithSlots,\n handleClickOnLink: (e) => this.handleClickOnNoSlotLink(e,\n nextDayWithSlots ? nextDayWithSlots : previousDayWithSlots,\n ),\n textNoSlots: nextDayWithSlots ? this.props.textNextSlot : this.props.textPreviousSlot,\n };\n // If no previous slots, just send a message to say there are no slots.\n } else {\n return { textNoSlots: this.props.textNoSlots };\n }\n }\n\n /**\n * Set the right amount of days depending on the width available for the calendar\n */\n public getNumberOfDays(): number {\n if (!this.calendarRef.current) {\n return this.state.nbDays;\n }\n const calendarWidth = this.calendarRef.current.offsetWidth;\n const nbDays = Math.floor(calendarWidth / 70);\n return nbDays > this.props.maxNbDays ? this.props.maxNbDays : nbDays;\n }\n\n /**\n * Order slots chronologically inside each individual day\n * The duplicates slots are marked and possibly hidden during this process.\n *\n * @param slots\n */\n public sortSlots(slots: Slot[]): Slot[] {\n let sortedSlots = [...slots]; // Avoid prop mutation\n\n sortedSlots = sortedSlots.sort((a, b) => {\n let result = 0;\n\n // Comparing start times if different\n if (a.start - b.start !== 0) {\n result = a.start - b.start;\n // If possible, we try to sort by endtime\n } else if (a.end && b.end && a.end - b.end !== 0) {\n result = a.end - b.end;\n }\n\n // If timestamps are the same, we mark the slots as duplicates\n if (!result) {\n a.hasDuplicate = true;\n b.hasDuplicate = true;\n\n // If the Calendar settings allows it, we hide one of the two duplicates\n a.hidden = !this.props.showDuplicates;\n }\n\n return result;\n });\n\n return sortedSlots;\n }\n\n /**\n * Refine data before using it in the view. Sort, filter, and calculate spaces for\n * each slots.\n *\n * @param slots\n */\n public processSlots(slots: { [name: number]: Slot[] }) {\n\n const processedSlots = {};\n\n for (const day in slots) {\n if (slots.hasOwnProperty(day)) {\n /*\n Make the method pure : The slots prop is deeply cloned into processedSlots\n so we don't mutate the prop directly.\n */\n processedSlots[day] = slots[day].map(slot => Object.assign({}, slot));\n\n // We calculate spaces occupied by each slot...\n processedSlots[day] = this.setSlotsSpace(\n // After sorting the slots by date...\n this.sortSlots(processedSlots[day])\n // And filtering the duplicates.\n .filter(slot => !slot.hidden),\n );\n }\n }\n\n return processedSlots;\n }\n\n /**\n * This function set a space on each slot. Spaces allow us to know if each slot\n * should be a part of the preview or not.\n *\n * @param slots\n */\n public setSlotsSpace(slots: Slot[]): Slot[] {\n const spacedSlots = [...slots];\n let space = 0;\n spacedSlots.forEach(slot => {\n // Default space for slot is 1\n space = space + 1;\n // If it has a end time, it's a bit bigger\n if (slot.end) {\n space = space + 0.5;\n }\n // If it shows a legend on small devices, it's a bit bigger\n if (slot.legend && isSmallScreen()) {\n space = space + 0.5;\n }\n slot.space = space;\n });\n return spacedSlots;\n }\n\n /**\n * Activate or desactivate preview mode that limits the number of slots displayed\n */\n public togglePreviewMode(e, callback = null) {\n this.setState({\n isPreview : !this.state.isPreview,\n }, callback);\n }\n}\n"
},
{
"filePath":"/pwd/styleguide/source/js/components/CharacterPicker/CharacterPicker-data.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"\n/**\n * Author: D B\n * DateTime: 19/05/17\n * Description: CharacterPicker data\n * Will look for ids in the DOM and initialise the corresponding\n * react elements.\n */\n\nimport * as React from 'react';\nimport { CharacterPicker } from './CharacterPicker';\n\nexport const characterPickers = [\n {\n reactElement : (\n <CharacterPicker\n onChange={(e, selected) => console.log(selected)}\n values=\"MTWTFSS\"\n />\n ),\n rootId: 'character-picker',\n },\n {\n reactElement : (\n <CharacterPicker\n values={[\n {\n character: 'A',\n data: 'test',\n label: 'Press enter to choose the option A',\n },\n {\n character: 'B',\n data: 'test',\n label: 'Press enter to choose the option B',\n },\n {\n character: 'C',\n label: 'Press enter to choose the option C',\n },\n ]}\n selected={[0, 2]}\n />\n ),\n rootId: 'character-picker-default-values',\n },\n];\n"
},
{
"filePath":"/pwd/styleguide/source/js/components/CharacterPicker/CharacterPicker.test.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"/**\n * Author: D B\n * DateTime: 19/05/21\n * Description: Tests for CharacterPicker\n */\n\nimport { mount, render, shallow } from 'enzyme';\nimport * as React from 'react';\nimport {\n getComponents,\n simpleSnapshots,\n} from '../../dsg-test-util';\nimport { isEmpty } from '../../dsg-util';\nimport { CharacterPicker } from './CharacterPicker';\nimport { characterPickers } from './CharacterPicker-data';\n\nconst components = getComponents(characterPickers, [\n 'character-picker',\n 'character-picker-default-values',\n]);\n\nsimpleSnapshots(components);\n\n// Test toggle open and toggle close\ntest('Select/Deselect elements', () => {\n const component = shallow(components[0].reactElement);\n const instance: CharacterPicker = component.instance() as CharacterPicker;\n expect(isEmpty(instance.state.selected)).toBeTruthy();\n\n // Simulate selection of first index\n instance.togglePickSelection(new CustomEvent('click'), 0, () => {\n expect(instance.state.selected[0]).toEqual(0);\n expect(component).toMatchSnapshot();\n\n // Simulate selection of third index\n instance.togglePickSelection(new CustomEvent('click'), 2, () => {\n expect(instance.state.selected.join('')).toEqual('02');\n expect(component).toMatchSnapshot();\n\n // Simulate deselection of first index\n instance.togglePickSelection(new CustomEvent('click'), 0, () => {\n expect(instance.state.selected[0]).toEqual(2);\n expect(component).toMatchSnapshot();\n });\n });\n });\n});\n"
},
{
"filePath":"/pwd/styleguide/source/js/components/CharacterPicker/CharacterPicker.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"\n/**\n * Author: D B\n * DateTime: 19/05/17\n * Description: CharacterPicker React component, allowing to pick a choice into a set\n * represented by simple characters.\n */\n\nimport * as React from 'react';\nimport {\n isArray,\n setCSSClasses,\n} from '../../dsg-util';\n\n// One Characterpick is one item of the set\nexport interface CharacterPick {\n character: string; // It must have a character\n data?: any; // Any kind of data can be attached to it.\n label?: string; // For accessibility purpose, add a label for screen readers only\n}\n\nexport interface CharacterPickerProps {\n /**\n * Can be a string or a array of CharacterPick.\n * The string will be transformed into an array of CharacterPick on render\n */\n values: any | CharacterPick[];\n /** Array of indexes. It designate which item is selected in the `values` prop by index */\n selected?: number[];\n /** Event handler triggered after a selection or deselection of a CharacterPick */\n onChange?: (e: Event, selected: number[]) => void;\n}\n\nexport interface CharacterPickerState {\n /** Array of indexes. It designate which item is selected in the `values` prop by index */\n selected: number[];\n /** `selected` Prop stored in a state cache */\n cachedSelected: number[];\n}\n\nexport class CharacterPicker extends React.Component<\n CharacterPickerProps,\n CharacterPickerState\n> {\n\n public static defaultProps: Partial<CharacterPickerProps> = {\n selected: [],\n values: [],\n };\n\n constructor(props: CharacterPickerProps) {\n super(props);\n\n this.state = {\n cachedSelected: props.selected,\n selected: props.selected,\n };\n }\n\n // --------------------------------- RENDER -------------------------------\n\n public render() {\n let picks = this.props.values;\n\n // If values are a string, we transform each character into a CharacterPick\n if (!isArray(picks)) {\n picks = String(picks).split('').map(val => {\n return {\n character: val,\n };\n });\n }\n\n return (\n <div className=\"dsg-Character-picker\">\n {picks.map((pick, i) => this.displayPick(pick, i))}\n </div>\n );\n }\n\n /**\n * Display individual item in the CharacterPicker\n *\n * @param pick: CharacterPick\n * @param i: number\n * @return ReactNode\n */\n public displayPick(pick: CharacterPick, i: number) {\n const isSelected = this.state.selected.indexOf(i) !== -1;\n\n // CharacterPick is 'active' only if its index is located in the `selected` state array.\n const pickClasses = setCSSClasses({\n 'active': isSelected,\n 'dsg-Character-pick': true,\n });\n\n return (\n <button\n role=\"checkbox\"\n aria-checked={isSelected}\n className={pickClasses}\n aria-label={pick.label}\n onClick={e => this.togglePickSelection(e, i)}\n key={'character-picker-' + i}>\n {pick.character}\n </button>\n );\n }\n\n // --------------------------------- COMPONENT LIFECYCLE -------------------------------\n\n /**\n * Allow the `selected` state to be controlled from the parent component\n */\n // tslint:disable-next-line:member-ordering\n public static getDerivedStateFromProps(props: CharacterPickerProps, state: CharacterPickerState) {\n let {selected} = state;\n // Compare if selected from props has changed\n if (state.cachedSelected !== props.selected) {\n // We want to allow selection change from prop change\n selected = props.selected;\n }\n\n return {\n cachedSelected: props.selected,\n selected,\n };\n }\n\n // --------------------------------- CUSTOM FUNCTIONS -------------------------------\n\n /**\n * Toggle the selection of CharacterPick that has been clicked\n *\n * @param e: Event\n * @param pickIndex : Number\n * @param callback : Function\n */\n public togglePickSelection(e, pickIndex: number, callback?: () => void) {\n\n // In case the component is in a form, we want to prevent any submit\n e.preventDefault();\n\n const selected = [...this.state.selected];\n const selectedIndex = selected.indexOf(pickIndex);\n\n // If the index cannot be find, we add it to the array\n if (selectedIndex === -1) {\n selected.push(pickIndex);\n // If it's already there, we remove it.\n } else {\n selected.splice(selectedIndex, 1);\n }\n\n // Call optional prop handler\n if (this.props.onChange) {\n this.props.onChange(e, selected);\n }\n\n this.setState({selected}, callback);\n }\n}\n"
},
{
"filePath":"/pwd/styleguide/source/js/components/ColorPicker/ColorPicker-data.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"/**\n * Author: D B\n * DateTime: 18/09/26\n * LastUpdated: 18/09/26\n * Description: Example datas for color-picker\n */\n\nimport * as React from 'react';\nimport { ColorPicker } from './ColorPicker';\n\nconst pickColorLog = (color) => {\n console.log('You picked a color : ', color.hex);\n};\n\nconst hoverColorLog = (color) => {\n console.log('You hovered a color : ', color.hex);\n};\n\nexport const colorPickers = [\n {\n reactElement : (\n <ColorPicker\n handleColorChange={pickColorLog}\n onSwatchHover={hoverColorLog}\n color=\"#db3e00\"/>\n ),\n rootId: 'color-picker-default',\n },\n];\n"
},
{
"filePath":"/pwd/styleguide/source/js/components/ColorPicker/ColorPicker.test.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"import { mount, render, shallow } from 'enzyme';\nimport * as React from 'react';\nimport {\n getComponents,\n simpleSnapshots,\n} from '../../dsg-test-util';\nimport { ColorPicker } from './ColorPicker';\nimport { colorPickers } from './ColorPicker-data';\n\nconst components = getComponents(colorPickers, [\n 'color-picker-default',\n]);\n\nsimpleSnapshots(components);\n\ntest('Color picker toggle test', () => {\n const component = shallow(components[0].reactElement);\n const instance: ColorPicker = component.instance() as ColorPicker;\n expect(instance.state.displayColorPicker).toBeFalsy();\n\n instance.togglePicker(new CustomEvent('click'), () => {\n expect(instance.state.displayColorPicker).toBeTruthy();\n expect(component).toMatchSnapshot();\n });\n});\n\ntest('Color picker close test', () => {\n const component = shallow(components[0].reactElement);\n const instance: ColorPicker = component.instance() as ColorPicker;\n expect(instance.state.displayColorPicker).toBeFalsy();\n\n instance.setState({displayColorPicker: true}, () => {\n expect(instance.state.displayColorPicker).toBeTruthy();\n instance.closePicker(new CustomEvent('click'), () => {\n expect(instance.state.displayColorPicker).toBeFalsy();\n expect(component).toMatchSnapshot();\n });\n });\n});\n"
},
{
"filePath":"/pwd/styleguide/source/js/components/ColorPicker/ColorPicker.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"/**\n * Author: D B\n * DateTime: 18/09/26\n * LastUpdated: 18/09/26\n * Description:\n * React Component : Discrete component that allows to pick a choice among a limited range\n * of colors.\n */\n\nimport * as React from 'react';\nimport { GithubPicker } from 'react-color';\n\nexport interface ColorPickerProps {\n /** Event handler after color is picked */\n handleColorChange?: (color, e: Event) => void;\n /** Radius of button (half its width) */\n buttonRadius?: number;\n\n // All the props below can be spread to the GithubPicker component\n /** Event handler on mouse over color (sends back color, event) */\n onSwatchHover?: (color, e: Event) => void;\n /** Hex value of the color we want to start with, or keep the ColorPicker synced with. */\n color?: string;\n /**\n * Colors displayed in the picker (hex) -\n * By default : ['#B80000', '#DB3E00', '#FCCB00', '#008B02', '#006B76',\n * '#1273DE', '#004DCF', '#5300EB', '#EB9694', '#FAD0C3', '#FEF3BD',\n * '#C1E1C5', '#BEDADC', '#C4DEF6', '#BED3F3', '#D4C4FB']\n */\n colors?: string[];\n /** Size of color picker */\n colorPickerWidth?: number;\n /** Set position of the popover relative to its triangle (tail). Either 'hide', 'top-left' or 'top-right'. */\n triangle?: string;\n}\n\nexport interface ColorPickerState {\n /*\n 1 : State of GithubPicker being displayed or not\n 2 : Color of button (last picked color, default color or synced color)\n */\n displayColorPicker: boolean; /* 1 */\n color: string; /* 2 */\n}\n\nexport class ColorPicker extends React.Component<\n ColorPickerProps,\n ColorPickerState\n> {\n public static defaultProps: Partial<ColorPickerProps> = {\n buttonRadius: 20,\n color: '#fff',\n colorPickerWidth: 200,\n handleColorChange: null,\n onSwatchHover: null,\n triangle: 'top-left',\n };\n\n constructor(props: ColorPickerProps) {\n super(props);\n\n this.state = {\n color: props.color,\n displayColorPicker: false,\n };\n\n this.displayGithubPicker = this.displayGithubPicker.bind(this);\n this.togglePicker = this.togglePicker.bind(this);\n this.closePicker = this.closePicker.bind(this);\n }\n\n // --------------------------------- RENDER -------------------------------\n\n public render() {\n\n const buttonStyle = {\n background: this.state.color,\n borderRadius: (this.props.buttonRadius * 2) + 'px',\n height: this.props.buttonRadius + 'px',\n width: this.props.buttonRadius + 'px',\n };\n\n return (\n <div className=\"dsg-Color-picker\">\n <div\n style={ buttonStyle }\n className=\"dsg-Color-picker__button\"\n onClick={ this.togglePicker } />\n { this.state.displayColorPicker ? this.displayGithubPicker() : null }\n </div>\n );\n }\n\n /**\n * Display Github Picker on popover. Click the overlay to make it disappear.\n */\n public displayGithubPicker() {\n return (\n <div className=\"dsg-Color-picker__popover\">\n <div\n className=\"dsg-Color-picker__overlay\"\n onClick={ this.closePicker }/>\n <GithubPicker\n { ...this.props}\n onChange={ this.handleColorChange }\n onChangeComplete={ this.handleColorChangeComplete } />\n </div>\n );\n }\n\n // --------------------------------- CUSTOM FUNCTIONS -------------------------------\n\n /**\n * Toggle display state of picker\n */\n public togglePicker = (e, callback?) => {\n e.stopPropagation();\n this.setState({\n displayColorPicker: !this.state.displayColorPicker,\n }, callback);\n }\n\n /**\n * Hide the color picker\n */\n public closePicker = (e, callback?) => {\n e.stopPropagation();\n this.setState({\n displayColorPicker: false,\n }, callback);\n }\n\n /**\n * Handle drag or click event in GithubPicker\n *\n * @param color\n * @param event\n */\n public handleColorChange = (color, e: Event) => {\n e.stopPropagation();\n }\n\n /**\n * Handle color after it has been picked in the GithubPicker\n *\n * @param color\n * @param event\n */\n public handleColorChangeComplete = (color, e: Event) => {\n // Call parent event handler if there is one\n if (this.props.handleColorChange) {\n this.props.handleColorChange(color, e);\n }\n\n // Change color of button and close picker\n this.setState({\n color: color.hex,\n displayColorPicker: false,\n });\n }\n}\n"
},
{
"filePath":"/pwd/styleguide/source/js/components/Loader/Loader-data.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"\n/**\n * Author: dmn-B\n * DateTime: 2019/11/5\n * Description: Loader test and showcase datas\n * Will look for ids in the DOM and initialise the corresponding\n * react elements. There is a showcase for this component, as the loader\n * needs to be triggered before it's displayed (or else it would create a lot of chaos\n * in the styleguide, the loader being on top of everything).\n */\n\nimport * as React from 'react';\nimport Loader from './Loader';\n\n/** This is a good base for a smart modal for every showcase */\nconst returnBasicLoader = that => {\n return <Loader {...that.state} />;\n};\n\n/* The first showcase type is a modal triggered by a button */\nexport class LoaderShowcaseButton extends React.Component<any, any> {\n\n constructor(props) {\n super(props);\n this.state = {\n loading: false,\n };\n }\n\n public render() {\n return (\n <div>\n <button\n onClick={() => this.setState({loading: true})}>\n Toggle loader\n </button>\n\n {returnBasicLoader(this)}\n </div>\n );\n }\n}\n\nexport const loaders = [\n {\n reactElement : (\n <LoaderShowcaseButton />\n ),\n rootId: 'loader-overlay',\n },\n];\n"
},
{
"filePath":"/pwd/styleguide/source/js/components/Loader/Loader.test.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"\n/**\n * Author: dmn-B\n * DateTime: 2019/11/5\n * Description: Tests for Loader component\n */\n\nimport { mount, render, shallow } from 'enzyme';\nimport * as React from 'react';\nimport {\n getComponents,\n simpleSnapshots,\n} from '../../dsg-test-util';\nimport { loaders } from './Loader-data';\nimport { LoaderShowcaseButton } from './Loader-data';\n\nconst components = getComponents(loaders, [\n 'loader-overlay',\n]);\n\nsimpleSnapshots(components);\n\ntest('Display/Hide centered modal', () => {\n const component = shallow(components[0].reactElement);\n const instance: LoaderShowcaseButton = component.instance() as LoaderShowcaseButton;\n expect(instance.state.loading).toBeFalsy();\n\n // Display loader\n instance.setState({loading: true}, () => {\n expect(instance.state.loading).toBeTruthy();\n expect(component).toMatchSnapshot();\n });\n});\n"
},
{
"filePath":"/pwd/styleguide/source/js/components/Loader/Loader.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"\n/**\n * Author: dmn-B\n * DateTime: 2019/11/5\n * Description: Loader React component\n */\n\nimport * as React from 'react';\n\nexport interface LoaderProps {\n /** If true, the loader will be displayed */\n loading: boolean;\n}\n\nexport default function Loader(props: LoaderProps) {\n if (!props.loading) {\n return null;\n }\n\n return (\n <div className=\"dsg-vhalign dsg-overlay-fixed dsg-overlay-clear\">\n <div className=\"dsg-loader-ring dsg-loader-ring--dark\">\n <div></div><div></div><div></div><div></div>\n </div>\n </div>\n );\n}\n"
},
{
"filePath":"/pwd/styleguide/source/js/components/SmartModal/SmartModal-data.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"\n/**\n * Author: D B\n * DateTime: 19/05/23\n * Description: SmartModal data\n * Will look for ids in the DOM and initialise the corresponding\n * react elements. There are several types of showcase for this component, as the modal\n * needs to be triggered before it's displayed (or else it would create a lot of chaos\n * in the styleguide, the modals being on top of everything).\n * Each showcase is a react component, stored in this file.\n */\n\nimport * as React from 'react';\nimport { cleanDispatchEvent } from '../../dsg-util';\nimport {\n SmartModal,\n SmartModalProps,\n} from './SmartModal';\n\n/* tslint:disable-next-line:max-line-length */\nconst bodyLorem = <p>Amet esse eiusmod veniam consectetur dolor in laborum deserunt mollit laboris ut sunt est cupidatat qui nulla laboris officia laborum ex aute incididunt excepteur incididunt amet excepteur dolore esse officia cupidatat nostrud cillum velit quis quis adipisicing occaecat tempor voluptate laborum cupidatat laborum sed ex tempor qui reprehenderit cillum nostrud aute aute consequat amet duis veniam labore sunt irure enim eu consequat consectetur non consequat anim dolore voluptate eiusmod ut commodo commodo veniam id veniam veniam fugiat fugiat sed exercitation excepteur velit dolore aliqua est voluptate minim sunt nisi culpa fugiat dolore ut reprehenderit magna ut non est aliquip nulla quis qui aute eu occaecat ut laborum consectetur quis cillum reprehenderit est eu elit laborum commodo elit nulla in id dolore incididunt commodo sit qui sed deserunt eu anim occaecat proident excepteur esse culpa enim elit ullamco nulla esse officia ut id quis do proident commodo nostrud aliqua minim proident ex ut ut exercitation dolor eu sit sed irure aute commodo nostrud id ad duis laborum consequat consectetur occaecat in magna in non id duis enim in do dolore sunt ut eu nisi commodo aute sed eiusmod in cillum non deserunt consectetur nisi nulla nisi minim sed in proident ut laboris nostrud reprehenderit sint consectetur ut commodo et id ex cupidatat fugiat tempor in labore.</p>;\n\n/** This is a good base for a smart modal for every showcase */\nconst returnBasicSmartModal = that => {\n return <SmartModal\n {...that.props}\n {...that.state}\n closeLabel=\"Close this message\"\n contentLabel=\"Modal example - press escape to exit modal\"\n onClose={() => that.setState({isOpen: false})}\n onAfterOpen={() => console.log('The modal has opened')}>\n {that.props.children}\n </SmartModal>;\n};\n\n/* The first showcase type is a modal triggered by a button */\nexport class ModalShowcaseButton extends React.Component<any, any> {\n\n constructor(props) {\n super(props);\n this.state = {\n isOpen: false,\n };\n }\n\n public render() {\n return (\n <div>\n <button\n onClick={() => this.setState({isOpen: true})}>\n Toggle modal\n </button>\n\n {returnBasicSmartModal(this)}\n </div>\n );\n }\n}\n\n/*\n This showcase will trigger a modal each time the user clicks inside a zone.\n It will also position the modal with the coordinates of the click.\n*/\n// tslint:disable-next-line:max-classes-per-file\nexport class ModalShowcaseZone extends React.Component<any, any> {\n\n constructor(props) {\n super(props);\n this.state = {\n isOpen: false,\n };\n }\n\n public render() {\n return (\n <div>\n <div\n id=\"smartmodal-zone\"\n className=\"dsg-vhalign\"\n onClick={e => this.setModalCoordinates(e.clientX, e.clientY)}\n style={{\n background: '#e3f2fd',\n height: '500px',\n textAlign: 'center',\n width: '100%',\n }}>\n Click anywhere in this zone to trigger a modal\n </div>\n {returnBasicSmartModal(this)}\n </div>\n );\n }\n\n public setModalCoordinates(x, y, callback?) {\n this.setState({\n isOpen: true,\n xOrigin: x,\n yOrigin: y,\n }, callback);\n }\n}\n\nexport const smartModals = [\n {\n reactElement : (\n <ModalShowcaseButton>\n {{\n // tslint:disable-next-line:max-line-length\n body: bodyLorem,\n head: <h5>This is my modal title</h5>,\n }}\n </ModalShowcaseButton>\n ),\n rootId: 'modal',\n },\n {\n reactElement : (\n <ModalShowcaseButton\n headClassName=\"dsg-modal__head dsg-modal__head--danger\">\n {{\n // tslint:disable-next-line:max-line-length\n body: bodyLorem,\n head: <h5>This is a modal with dangerous content</h5>,\n }}\n </ModalShowcaseButton>\n ),\n rootId: 'modal-danger',\n },\n {\n reactElement : (\n <ModalShowcaseZone\n overlayColor=\"transparent\"\n // showCloseButton={false}\n style={{\n height: '400px',\n width: '300px',\n }}\n xMarginOrigin={20}\n yMarginOrigin={20}\n >\n {{\n // tslint:disable-next-line:max-line-length\n body: bodyLorem,\n }}\n </ModalShowcaseZone>\n ),\n rootId: 'modal-with-origin-point',\n },\n];\n"
},
{
"filePath":"/pwd/styleguide/source/js/components/SmartModal/SmartModal.test.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"/**\n * Author: D B\n * DateTime: 19/05/29\n * Description: Tests for SmartModal\n */\n\nimport { mount, render, shallow } from 'enzyme';\nimport * as React from 'react';\nimport {\n getComponents,\n simpleSnapshots,\n} from '../../dsg-test-util';\nimport { SmartModal } from './SmartModal';\nimport { ModalShowcaseButton, ModalShowcaseZone, smartModals } from './SmartModal-data';\n\nconst components = getComponents(smartModals, [\n 'modal',\n 'modal-danger',\n 'modal-with-origin-point',\n]);\n\nsimpleSnapshots(components);\n\ntest('Display/Hide centered modal', () => {\n const component = shallow(components[0].reactElement);\n const instance: ModalShowcaseButton = component.instance() as ModalShowcaseButton;\n expect(instance.state.isOpen).toBeFalsy();\n\n // Display modal\n instance.setState({isOpen: true}, () => {\n expect(instance.state.isOpen).toBeTruthy();\n expect(component).toMatchSnapshot();\n\n // Close modal\n instance.setState({isOpen: false}, () => {\n expect(instance.state.isOpen).toBeFalsy();\n expect(component).toMatchSnapshot();\n });\n });\n});\n\ntest('Display and hide modals with different origin points', () => {\n const component = shallow(components[2].reactElement);\n const instance: ModalShowcaseZone = component.instance() as ModalShowcaseZone;\n expect(instance.state.isOpen).toBeFalsy();\n const width = document.body.offsetWidth;\n const height = document.body.offsetHeight;\n\n // Coordinates for smartmodal zone to test\n const coordinates = [\n [0, 0],\n [width, 0],\n [0, height],\n [width, height],\n [width / 2, height / 2],\n ];\n\n testCoordinates(instance, component, coordinates, 0);\n});\n\n// Test directly the component without any showcase\ntest('Smart Modal custom functions', () => {\n const component = shallow(\n <SmartModal\n isOpen={true}\n closeLabel=\"Close this message\"\n onClose={() => console.log('modal display has close')}\n onAfterOpen={() => console.log('The modal has opened')}>\n {{\n // tslint:disable-next-line:max-line-length\n body: <div>Amet esse eiusmod veniam consectetur dolor in laborum deserunt mollit laboris ut sunt est cupidatat qui nulla laboris officia laborum ex aute incididunt excepteur incididunt amet excepteur dolore esse officia cupidatat nostrud cillum velit quis quis adipisicing occaecat tempor voluptate laborum cupidatat laborum sed ex tempor qui reprehenderit cillum nostrud aute aute consequat amet duis veniam labore sunt irure enim eu consequat consectetur non consequat anim dolore voluptate eiusmod ut commodo commodo veniam id veniam veniam fugiat fugiat sed exercitation excepteur velit dolore aliqua est voluptate minim sunt nisi culpa fugiat dolore ut reprehenderit magna ut non est aliquip nulla quis qui aute eu occaecat ut laborum consectetur quis cillum reprehenderit est eu elit laborum commodo elit nulla in id dolore incididunt commodo sit qui sed deserunt eu anim occaecat proident excepteur esse culpa enim elit ullamco nulla esse officia ut id quis do proident commodo nostrud aliqua minim proident ex ut ut exercitation dolor eu sit sed irure aute commodo nostrud id ad duis laborum consequat consectetur occaecat in magna in non id duis enim in do dolore sunt ut eu nisi commodo aute sed eiusmod in cillum non deserunt consectetur nisi nulla nisi minim sed in proident ut laboris nostrud reprehenderit sint consectetur ut commodo et id ex cupidatat fugiat tempor in labore.</div>,\n head: <span>This is my modal title</span>,\n }}\n </SmartModal>,\n );\n\n const instance: SmartModal = component.instance() as SmartModal;\n expect(instance.state.isOpen).toBeTruthy();\n expect(component).toMatchSnapshot();\n\n instance.closeModal(() => {\n expect(instance.state.isOpen).toBeFalsy();\n expect(component).toMatchSnapshot();\n });\n});\n\n/**\n * Will simulate clicks in all the coordinates sent on the smartmodal showcase with a zone\n *\n * @param instance\n * @param component\n * @param coordinates : Array\n * @param i : number\n */\nconst testCoordinates = (instance, component, coordinates, i: number) => {\n\n // We open the modal with a certain origin point\n instance.setModalCoordinates(coordinates[i][0], coordinates[i][1], () => {\n expect(instance.state.isOpen).toBeTruthy();\n expect(component).toMatchSnapshot();\n\n // We then close the modal\n instance.setState({isOpen: false}, () => {\n expect(instance.state.isOpen).toBeFalsy();\n\n // Reopen and test the modal again with the next coordinates as an origin point\n if (i + 1 < coordinates.length) {\n testCoordinates(instance, component, coordinates, i + 1);\n }\n });\n });\n};\n"
},
{
"filePath":"/pwd/styleguide/source/js/components/SmartModal/SmartModal.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"\n/**\n * Author: D B\n * DateTime: 19/05/23\n * Description: SmartModal react component\n */\n\nimport * as React from 'react';\nimport * as ReactModal from 'react-modal';\nimport {\n debounce,\n getCharCode,\n isNumber,\n isUndefined,\n setCSSClasses,\n} from '../../dsg-util';\n\nexport interface SmartModalProps {\n /** The SmartModal's children should be divided between head and body. For instance {{ head: 'foo', body:'bar' }} */\n children: {\n head?: React.ReactNode | string;\n body?: React.ReactNode | string;\n };\n /** Label for scroll button for screen readers */\n closeLabel?: string;\n /** Label read by screen readers when the modal is opened */\n contentLabel?: string;\n /** Defines CSS classes for the head of the modal */\n headClassName: string;\n /** Defines if the modal is displayed or not */\n isOpen: boolean;\n /** Function triggered just after the modal has opened */\n onAfterOpen?: () => void;\n /** Function triggered when the modal closes */\n onClose?: () => void;\n /** Background overlay color value */\n overlayColor?: string;\n /**\n * Display or not the cross button on top right of the modal.\n * It's displayed only if a head child is present as well.\n */\n showCloseButton?: boolean;\n /** Custom inline style that will be applied to the modal node (`dsg-modal`) */\n style?: React.CSSProperties;\n /** Defines if the modal should be horizontally centered */\n xCentered?: boolean;\n /** Defines if the modal should be vertically centered */\n yCentered?: boolean;\n /** Set the modal position to either right or left of this point, depending on which side has the most space. */\n xOrigin?: number;\n /** Set the modal position to either top or bottom of this point, depending on which side has the most space. */\n yOrigin?: number;\n /** Margin separating the modal from the xOrigin point */\n xMarginOrigin?: number;\n /** Margin separating the modal from the yOrigin point */\n yMarginOrigin?: number;\n}\n\nexport interface SmartModalState {\n /** Returns true if the modal nodes are accessible in the DOM for JS manipulation */\n nodeIsAccessible: boolean;\n /** Defines if the modal is displayed or not (last known prop value) */\n isOpenCached: boolean;\n /** Defines if the modal is displayed or not */\n isOpen: boolean;\n}\n\nexport class SmartModal extends React.Component<\n SmartModalProps,\n SmartModalState\n> {\n\n public static defaultProps: Partial<SmartModalProps> = {\n closeLabel: '',\n headClassName: 'dsg-modal__head',\n isOpen: false,\n overlayColor: 'rgba(0, 0, 0, 0.25)',\n showCloseButton: true,\n style: {},\n xCentered: true,\n yCentered: true,\n };\n\n private modalRef;\n private readonly headRef: React.RefObject<HTMLDivElement>;\n private readonly bodyRef: React.RefObject<HTMLDivElement>;\n\n constructor(props: SmartModalProps) {\n super(props);\n this.modalRef = React.createRef();\n this.headRef = React.createRef();\n this.bodyRef = React.createRef();\n\n this.state = {\n isOpen: props.isOpen,\n isOpenCached: props.isOpen,\n nodeIsAccessible: false,\n };\n\n this.handleOpen = this.handleOpen.bind(this);\n this.handleResizeEvent = this.handleResizeEvent.bind(this);\n }\n\n // --------------------------------- RENDER -------------------------------\n\n public render() {\n const {xOrigin, yOrigin} = this.props;\n\n // The modal style first takes in all the custom styling set in the props.\n const modalStyle = {\n ...this.props.style,\n };\n\n /*\n If an origin point has been set in the props, it will be used to override\n the top and left CSS properties of the modal.\n */\n if (this.state.nodeIsAccessible && (xOrigin || yOrigin)) {\n const originPoint = this.calculateOriginPosition(xOrigin, yOrigin);\n\n if (originPoint.left) {\n modalStyle.left = originPoint.left;\n }\n\n if (originPoint.top) {\n modalStyle.top = originPoint.top;\n }\n }\n\n // Classes for the overlay, the background layer below the modal\n const overlayClasses = setCSSClasses({\n 'dsg-align': this.props.xCentered && !modalStyle.left, // align horizontally\n 'dsg-modal__overlay': true,\n 'dsg-overlay-fixed': true,\n 'dsg-valign': this.props.yCentered && !modalStyle.top, // align vertically\n });\n\n return (\n <ReactModal\n overlayClassName={overlayClasses}\n className=\"dsg-modal\"\n style={{\n content: modalStyle,\n overlay: {\n backgroundColor: this.props.overlayColor,\n },\n }}\n contentRef={node => this.modalRef = node}\n isOpen={this.state.isOpen}\n onAfterOpen={this.handleOpen}\n onRequestClose={() => this.closeModal()}\n contentLabel={this.props.contentLabel}>\n\n <div className=\"dsg-rel-wrapper\">\n\n {\n this.props.showCloseButton && this.props.children.head ?\n this.displayCloseButton() : null\n }\n\n { this.props.children.head ? this.displayHead() : null }\n\n { this.props.children.body ? this.displayBody() : null }\n\n </div>\n\n </ReactModal>\n );\n }\n\n /**\n * Cross close button at the top right of the modal\n */\n public displayCloseButton() {\n return (\n <a\n className=\"dsg-modal__close\"\n role=\"button\"\n aria-label={this.props.closeLabel}\n onKeyPress={(e) => this.handleKeyPressOnClose(e)}\n onClick={() => this.closeModal()}\n tabIndex={0}>\n <i className=\"dsg-icon dsg-icon-close\"></i>\n </a>\n );\n }\n\n /**\n * Header of modal\n */\n public displayHead() {\n return (\n <div\n ref={this.headRef}\n className={this.props.headClassName}>\n {this.props.children.head}\n </div>\n );\n }\n\n /**\n * Contents of modal\n */\n public displayBody() {\n\n let bodyStyle = {};\n\n // Checking which height and overflow state should be applied to body node\n if (this.state.nodeIsAccessible && this.modalRef) {\n const containerHeight = this.modalRef.offsetHeight;\n const head = this.headRef.current;\n const headHeight = head ? head.offsetHeight : 0; // Head child is optional\n let bodyHeight = this.bodyRef.current.offsetHeight;\n\n /*\n Content is overflowing if head and body heights are bigger than the container\n The \"+ 1\" has been added to prevent half-pixels from ruining the comparison\n */\n const contentIsOverflowing = bodyHeight + headHeight > containerHeight + 1;\n\n // The body height will just be what's left of the container's height after we\n // substract the head's height from it.\n bodyHeight = contentIsOverflowing ? containerHeight - headHeight : bodyHeight;\n\n bodyStyle = {\n height: bodyHeight,\n overflowY: contentIsOverflowing ? 'scroll' : 'visible',\n };\n }\n\n return (\n <div\n className=\"dsg-modal__body-wrapper\"\n style={bodyStyle}\n ref={this.bodyRef}>\n <div className=\"dsg-modal__body\">\n {this.props.children.body}\n </div>\n </div>\n );\n }\n\n // --------------------------------- COMPONENT LIFECYCLE -------------------------------\n\n public componentDidUpdate(prevProps: SmartModalProps, prevState: SmartModalState) {\n\n /*\n This specific condition happens after a resize while the modal was open.\n Now that the modal's body height is at its 'original' size, we can rerender the\n component and decides if the content is overflowing or not.\n */\n if (prevState.nodeIsAccessible && !this.state.nodeIsAccessible && this.state.isOpen) {\n this.setState({\n nodeIsAccessible: this.state.isOpen,\n });\n }\n }\n\n public componentDidMount() {\n window.addEventListener('resize', debounce(this.handleResizeEvent, 50));\n }\n\n public componentWillUnmount() {\n window.removeEventListener('resize', debounce(this.handleResizeEvent, 50));\n }\n\n // tslint:disable-next-line:member-ordering\n public static getDerivedStateFromProps(props: SmartModalProps, state: SmartModalState) {\n let {isOpen} = state;\n // Compare if isOpen from props has changed\n if (state.isOpenCached !== props.isOpen) {\n // We want to allow opening the modal from prop change\n isOpen = props.isOpen;\n }\n\n return {\n isOpen,\n isOpenCached: props.isOpen,\n };\n }\n\n // --------------------------------- CUSTOM FUNCTIONS -------------------------------\n\n /**\n * Event handler after the modal has been opened\n */\n public handleOpen() {\n if (this.props.onAfterOpen) {\n this.props.onAfterOpen();\n }\n\n this.setState({\n nodeIsAccessible: true,\n });\n }\n\n /**\n * Handle resize event\n */\n public handleResizeEvent() {\n /*\n At each resize, we need to cancel the custom height style that we apply to the modal\n so we can check if its contents are overflowing the maximum height or not.\n We then retoggle this state in the componentDidUpdate method to reapply the\n right height and overflow style.\n */\n this.setState({\n nodeIsAccessible: false,\n });\n }\n\n /**\n * Handle key press event on close button\n *\n * @param e: Event\n */\n public handleKeyPressOnClose(e: React.KeyboardEvent) {\n const code = getCharCode(e);\n\n // If key is enter or space, we hide the modal\n if (code === 32 || code === 13) {\n this.closeModal();\n }\n }\n\n /**\n * Returns CSS position of the modal (left and top) by using x and y coordinates as\n * an origin point. The modal will be placed where it has the most space available,\n * and will be centered if there is not enough space.\n *\n * @param x: number\n * @param y: number\n * @return {left: string, top: string}\n */\n public calculateOriginPosition(x: number, y: number) {\n const {xMarginOrigin, yMarginOrigin} = this.props;\n const left = isNumber(x) ? this.calculatePositionCoordinate('Width', x, xMarginOrigin) : null;\n const top = isNumber(y) ? this.calculatePositionCoordinate('Height', y, yMarginOrigin) : null;\n return {left, top};\n }\n\n /**\n * Return one coordinate of the modal position, either the 'top' or 'left' value\n * of the modal.\n *\n * @param side: string\n * @param origin: number\n * @param margin: number\n * @return string\n */\n public calculatePositionCoordinate(side: string, origin: number, margin: number = 0) {\n const modalSide = this.modalRef['offset' + side];\n const pageSide = window['inner' + side];\n\n // Space available at one side of the origin point...\n const negativeSpace = origin - margin - modalSide;\n // ...and at the other side of the origin point\n const positiveSpace = pageSide - origin - margin - modalSide;\n\n const polarityIsNegative = negativeSpace >= positiveSpace;\n const position = polarityIsNegative ? negativeSpace : origin + margin;\n\n /*\n If not enough space, we let the modal be 'naturally' positionned.\n (If xCentered and yCentered props are true, it will be centered)\n */\n if (position + modalSide >= pageSide || position < 0) {\n return null;\n }\n\n return position + 'px';\n }\n\n /**\n * Close modal\n *\n * @param callback: function\n */\n public closeModal(callback?: () => void) {\n if (this.props.onClose) {\n this.props.onClose();\n }\n\n this.setState({\n isOpen: false,\n nodeIsAccessible: false,\n }, callback);\n }\n}\n"
},
{
"filePath":"/pwd/styleguide/source/js/components/TabTextarea/TabTextarea-data.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"\n/**\n * Author: dmn-B\n * DateTime: 2019/11/7\n * Description: TabTextarea test and showcase datas\n * Will look for ids in the DOM and initialise the corresponding\n * react elements.\n */\n\nimport * as React from 'react';\nimport { TabTextarea } from './TabTextarea';\n\nexport const tabTextareas = [\n {\n reactElement : (\n <TabTextarea\n // onChange={(tab) => console.log(`Tab message ${tab.id} has been changed with ${tab.textareaContent}`)}\n placeholder=\"Insert text here...\"\n rows={5}\n tabs={[\n {\n id: 'tab1',\n tabContent: 'Tab-1',\n },\n {\n id: 'tab2',\n tabContent: 'Tab-2',\n textareaContent: 'Message 2',\n },\n {\n id: 'tab3',\n placeholder: 'No help is coming...',\n tabContent: <span><i className=\"dsg-icon dsg-icon-help\"></i> Help</span>,\n },\n ]}\n />\n ),\n rootId: 'tab-textarea',\n },\n];\n"
},
{
"filePath":"/pwd/styleguide/source/js/components/TabTextarea/TabTextarea.test.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"\n/**\n * Author: dmn-B\n * DateTime: 2019/11/7\n * Description: Tests for TabTextarea component\n *\n * TODO: Make more tests\n */\n\nimport { mount, render, shallow } from 'enzyme';\nimport * as React from 'react';\nimport {\n getComponents,\n simpleSnapshots,\n} from '../../dsg-test-util';\nimport { TabTextarea } from './TabTextarea';\nimport { tabTextareas } from './TabTextarea-data';\n\nconst components = getComponents(tabTextareas, [\n 'tab-textarea',\n]);\n\nsimpleSnapshots(components);\n"
},
{
"filePath":"/pwd/styleguide/source/js/components/TabTextarea/TabTextarea.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"\n/**\n * Author: dmn-B\n * DateTime: 2019/11/7\n * Description: TabTextarea React component\n */\n\nimport * as React from 'react';\nimport { Tab, TabList, TabPanel, Tabs } from 'react-tabs';\n\ninterface TabProp {\n id?: string;\n placeholder?: string;\n tabContent: string | JSX.Element;\n textareaContent?: string;\n}\n\nexport interface TabTextareaProps {\n /**\n * There on TabProp object per Tab. It will have data about the different tabs:\n * - placeholder: optional string for the textarea placeholder\n * - tabContent: string or HTMLElement to display inside the tab\n * - textareaContent: message to display inside the textarea input\n */\n tabs: TabProp[];\n /** General placeholder for all textareas in every tabs, that doesn't have one set */\n placeholder?: string;\n /**\n * Event handler for when one textarea content is modified. It sends as parameters\n * the tab that has been modified, its index inside the tabs prop, and the tabs prop\n * modified.\n */\n onChange?: (tab: TabProp, i: number, tabs: TabProp[]) => void;\n /** Define the height of the textarea, in rows */\n rows?: number;\n}\n\nexport interface TabTextareaState {\n tabs: TabProp[];\n tabsCached: TabProp[];\n}\n\nexport class TabTextarea extends React.Component<\n TabTextareaProps,\n TabTextareaState\n> {\n\n public static defaultProps: Partial<TabTextareaProps> = {\n placeholder: 'Insert text here...',\n rows: 5,\n };\n\n constructor(props: TabTextareaProps) {\n super(props);\n\n this.state = {\n tabs: props.tabs || [],\n tabsCached: props.tabs,\n };\n\n this.displayTab = this.displayTab.bind(this);\n this.displayTextarea = this.displayTextarea.bind(this);\n }\n\n // --------------------------------- RENDER -------------------------------\n\n public render() {\n return (\n <Tabs className=\"react-tabs react-tabs--textarea\">\n {/* LANGUAGE SELECTOR */}\n <TabList className=\"react-tabs__tab-list\">\n {this.props.tabs.map(this.displayTab)}\n </TabList>\n\n {/* MESSAGES */}\n {this.props.tabs.map(this.displayTextarea)}\n </Tabs>\n );\n }\n\n public displayTab(tab: TabProp, i: number) {\n return (\n <Tab key={'tab-' + i}>\n <div className=\"dsg-valign dsg-line-height-normalize\">\n {tab.tabContent}\n </div>\n </Tab>\n );\n }\n\n public displayTextarea(tab: TabProp, i: number) {\n return (\n <TabPanel key={'tabpanel-' + i}>\n <textarea\n rows={this.props.rows}\n className=\"dsg-tabs-textarea\"\n placeholder={tab.placeholder || this.props.placeholder}\n value={tab.textareaContent || ''}\n onChange={(e) => this.handleTextareaChange(e.target.value, i)}\n />\n </TabPanel>\n );\n }\n\n // --------------------------------- COMPONENT LIFECYCLE -------------------------------\n\n // tslint:disable-next-line:member-ordering\n public static getDerivedStateFromProps(props: TabTextareaProps, state: TabTextareaState) {\n let {tabs} = state;\n\n if (state.tabsCached !== props.tabs) {\n tabs = props.tabs;\n }\n\n return {\n tabs,\n tabsCached: props.tabs,\n };\n }\n\n // --------------------------------- CUSTOM FUNCTIONS -------------------------------\n\n public handleTextareaChange(value: string, i: number) {\n const tabs = [...this.state.tabs];\n tabs[i].textareaContent = value;\n\n if (this.props.onChange) {\n this.props.onChange(tabs[i], i, tabs);\n } else {\n this.setState({tabs});\n }\n }\n}\n"
},
{
"filePath":"/pwd/styleguide/source/js/components/ToggableBox/ToggableBox-data.test.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"/**\n * Author: D B\n * DateTime: 18/10/02\n * LastUpdated: 18/10/02\n * Description: Test for ToggableBox\n */\n\nimport { mount, render, shallow } from 'enzyme';\nimport * as React from 'react';\nimport {\n getComponents,\n simpleSnapshots,\n} from '../../dsg-test-util';\nimport { ToggableBox } from './ToggableBox';\nimport { toggableBoxes } from './ToggableBox-data';\n\nconst components = getComponents(toggableBoxes, [\n 'toggable-box-closed',\n 'toggable-box-opened',\n 'toggable-box-loading',\n 'toggable-box-error',\n 'toggable-box-disabled',\n 'toggable-box-empty',\n 'toggable-box-colored',\n]);\n\nsimpleSnapshots(components);\n\n// Test toggle open and toggle close\ntest('Toggle box', () => {\n const component = shallow(components[0].reactElement); // Closed box\n const instance: ToggableBox = component.instance() as ToggableBox;\n expect(instance.state.toggled).toBeFalsy();\n // Simulate toggle open\n instance.toggleBody(new CustomEvent('click'), () => {\n expect(instance.state.toggled).toBeTruthy();\n expect(component).toMatchSnapshot();\n // Simulate toggle close\n instance.toggleBody(new CustomEvent('click'), () => {\n expect(instance.state.toggled).toBeFalsy();\n expect(component).toMatchSnapshot();\n });\n });\n});\n\n/*\n Missing tests :\n - Check after component has mounted\n - Check after component has dismounted\n - Check after updated\n*/\n"
},
{
"filePath":"/pwd/styleguide/source/js/components/ToggableBox/ToggableBox-data.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"/**\n * Author: D B\n * DateTime: 18/09/21\n * Description: Example datas for toggableBoxes\n */\n\nimport * as React from 'react';\nimport { ColorPicker } from '../ColorPicker/ColorPicker';\nimport { ToggableBox } from './ToggableBox';\n\nconst simpleLorem = 'In fugiat laborum velit culpa sed sint ea veniam sed sit' +\n 'dolor consequat proident amet ullamco voluptate in sint cillum magna et eu' +\n 'sunt cillum minim laborum est.';\n\nconst clickEdit = (e) => {\n e.stopPropagation();\n console.log('Edit has been clicked');\n};\n\nconst clickDelete = (e) => {\n e.stopPropagation();\n console.log('Delete has been clicked');\n};\n\nconst resource = (\n <div className=\"dsg-Resource\">\n <ToggableBox toggled={true}>\n {{\n head: (\n <div className=\"dsg-valign dsg-space-between dsg-full-width\">\n <div className=\"dsg-valign\">\n <span>Resource example</span>\n <span className=\"dsg-semi-mg-left dsg-valign\">\n <ColorPicker\n triangle=\"hide\"\n buttonRadius={15} />\n </span>\n </div>\n <div>\n <span\n data-dsg-tooltip=\"Edit\"\n className=\"dsg-tooltip dsg-tooltip--w dsg-semi-mg-right\">\n <button\n className=\"dsg-btn dsg-btn--small\"\n onClick={clickEdit}>\n <i className=\"dsg-icon dsg-icon-pencil\"></i>\n </button>\n </span>\n <span\n data-dsg-tooltip=\"Delete\"\n className=\"dsg-tooltip dsg-tooltip--w dsg-semi-mg-right\">\n <button\n className=\"dsg-btn dsg-btn--small dsg-btn--error\"\n onClick={clickDelete}>\n <i className=\"dsg-icon dsg-icon-bin\"></i>\n </button>\n </span>\n </div>\n </div>\n ),\n // tslint:disable-next-line:object-literal-sort-keys\n body: (\n <div>\n <div className=\"dsg-form-inline\">\n <input\n placeholder=\"My Ressource name\"\n type=\"text\"\n className=\"dsg-textinput\" />\n <select multiple className=\"dsg-multiselect\">\n <option value=\"My practice 1\">My practice 1</option>\n <option value=\"My practice 2\">My practice 2</option>\n <option value=\"My practice 3\">My practice 3</option>\n <option value=\"My practice 4\">My practice 4</option>\n </select>\n <button className=\"dsg-btn dsg-btn--success\">Save</button>\n </div>\n <div className=\"dsg-select__wrapper dsg-mg-top\">\n <select>\n <option value=\"option1\">Option 1</option>\n <option value=\"option2\">Option 2 is a bit longer</option>\n <option value=\"option3\">Option 3 push it a little a bit too far</option>\n </select>\n </div>\n </div>\n ),\n }}\n </ToggableBox>\n </div>\n);\n\nexport const toggableBoxes = [\n {\n reactElement : (\n <ToggableBox>\n {{\n body: simpleLorem,\n head: 'ToggableBox initiated in a closed state',\n }}\n </ToggableBox>\n ),\n rootId: 'toggable-box-closed',\n },\n {\n reactElement : (\n <ToggableBox toggledDefault={true}>\n {{\n body: simpleLorem,\n head: 'ToggableBox initiated in a opened state',\n }}\n </ToggableBox>\n ),\n rootId: 'toggable-box-opened',\n },\n {\n reactElement : (\n <ToggableBox loading={true}>\n {{\n body: simpleLorem,\n head: 'Loading ToggableBox',\n }}\n </ToggableBox>\n ),\n rootId: 'toggable-box-loading',\n },\n {\n reactElement : (\n <ToggableBox hasError={true}>\n {{\n body: simpleLorem,\n head: 'ToggableBox has error',\n }}\n </ToggableBox>\n ),\n rootId: 'toggable-box-error',\n },\n {\n reactElement : (\n <ToggableBox disabled={true}>\n {{\n body: simpleLorem,\n head: 'Disabled ToggableBox',\n }}\n </ToggableBox>\n ),\n rootId: 'toggable-box-disabled',\n },\n {\n reactElement : (\n <ToggableBox>\n {{\n head: 'Empty ToggableBox',\n }}\n </ToggableBox>\n ),\n rootId: 'toggable-box-empty',\n },\n {\n reactElement : (\n <ToggableBox\n color=\"#383838\">\n {{\n body: simpleLorem,\n head: 'ToggableBox with custom color',\n }}\n </ToggableBox>\n ),\n rootId: 'toggable-box-colored',\n },\n {\n reactElement : (\n <ToggableBox\n className=\"dsg-Toggable-box--neutral\">\n {{\n body: simpleLorem,\n head: 'ToggableBox with a neutral style',\n }}\n </ToggableBox>\n ),\n rootId: 'toggable-box-neutral',\n },\n\n // ******** RESOURCE EXAMPLE ********\n {\n reactElement : resource,\n rootId: 'resource-example',\n },\n {\n reactElement : resource,\n rootId: 'resource-example-2',\n },\n];\n"
},
{
"filePath":"/pwd/styleguide/source/js/components/ToggableBox/ToggableBox.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"/**\n * Author: D B\n * DateTime: 18/09/21\n * Description:\n * React component - A simple block with head and body section. The body can be\n * hid or displayed by clicking some parts of the head section.\n * This component's 'children' prop is an object with a 'head' and 'body' attributes,\n * so you can set its contents to the appropriate places.\n */\n\nimport * as React from 'react';\nimport {\n debounce,\n isVisible,\n setCSSClasses,\n} from '../../dsg-util';\n\nexport interface ToggableBoxProps {\n /** Allow to control the toggled state of the ToggableBox from the parent component */\n toggled?: boolean;\n /** Indicates the initial value of the toggled state */\n toggledDefault?: boolean;\n /** A disabled ToggableBox looks gray and ugly */\n disabled?: boolean;\n /** A loading ToggableBox has a loader instead of an arrow and is transparent */\n loading?: boolean;\n /** If toggableBox hasError, an icon and red color will highlight this information */\n hasError?: boolean;\n /** A toggableBox's children should be divided between head and body. For instance {{ head: 'foo', body:'bar' }} */\n children: {\n head?: React.ReactNode;\n body?: React.ReactNode;\n };\n /** onClick handler, might for example toggle the box */\n onClick?: (e) => void;\n /** Hex color for ToggableBox. Header and border will use this color. */\n color?: string;\n /** Custom CSS classes attribute to pass to the ToggableBox on the parent node */\n className?: string;\n /** Number: Animation duration in seconds */\n animationDuration?: number;\n}\n\nexport interface ToggableBoxState {\n /** Open/closed state of box */\n toggled: boolean;\n /** 'toggled' Prop stored in a state cache */\n cachedToggled: boolean;\n /** Returns true if dynamic height of body is accessible in the DOM */\n bodyHeightSet: boolean;\n /** Returns true is the animation is currently happening */\n animationOn: boolean;\n}\n\nexport class ToggableBox extends React.Component<ToggableBoxProps, ToggableBoxState> {\n\n public static defaultProps: Partial<ToggableBoxProps> = {\n animationDuration: 0.3,\n className: '',\n disabled: false,\n hasError: false,\n loading: false,\n toggled: false,\n };\n\n private readonly headRef: React.RefObject<HTMLDivElement>;\n private readonly bodyRef: React.RefObject<HTMLDivElement>;\n\n constructor(props: ToggableBoxProps) {\n super(props);\n this.headRef = React.createRef();\n this.bodyRef = React.createRef();\n\n this.state = {\n animationOn: false,\n bodyHeightSet: false,\n cachedToggled: props.toggled,\n toggled: props.toggledDefault || props.toggled || false, // Is box's body visible ?\n };\n\n this.displayIcons = this.displayIcons.bind(this);\n this.handleClick = this.handleClick.bind(this);\n this.handleResizeEvent = this.handleResizeEvent.bind(this);\n this.toggleBody = this.toggleBody.bind(this);\n this.stopAnimation = this.stopAnimation.bind(this);\n }\n\n // --------------------------------- RENDER -------------------------------\n\n public render() {\n const toggableBoxClasses = {\n 'dsg-Toggable-box--disabled': this.props.disabled,\n 'dsg-Toggable-box--empty': !this.props.children.body,\n 'dsg-Toggable-box--hasError': this.props.hasError,\n 'dsg-Toggable-box--loading': this.props.loading,\n 'dsg-Toggable-box--toggled': this.state.toggled,\n 'dsg-Toggable-box__wrapper': true,\n };\n\n this.props.className.split(' ').forEach((className: string) => {\n if (className) {\n toggableBoxClasses[className] = true;\n }\n });\n\n const headNode = this.headRef.current; // Is null on first render\n const bodyNode = this.bodyRef.current; // idem\n\n const styleWrapper = {\n minHeight: headNode ? headNode.offsetHeight : '',\n overflowY: this.state.animationOn ? 'hidden' : 'visible',\n } as React.CSSProperties;\n\n const styleBody = {\n // Top is out of range on first render because we can't access yet it's real height\n borderColor: this.props.color || '',\n marginTop: this.state.bodyHeightSet ? (\n !this.state.toggled ? -(bodyNode.offsetHeight) : '0px'\n ) : '-9999px',\n // We don't want any transition on the first render\n transitionDuration: this.state.bodyHeightSet ? this.props.animationDuration + 's' : '',\n visibility: !this.state.toggled && !this.state.animationOn ? 'hidden' : '',\n } as React.CSSProperties;\n\n const styleHead = {\n background: this.props.color || '',\n };\n\n return (\n <div className={setCSSClasses(toggableBoxClasses)} style={styleWrapper}>\n {/* Head */}\n <div\n style={styleHead}\n onClick={this.handleClick}\n ref={this.headRef}\n className=\"dsg-Toggable-box__head\">\n\n {/* Arrow or loading icon */}\n {this.displayIcons()}\n\n {/* Contents of head */}\n {this.props.children.head}\n\n </div>\n\n {/* Body */}\n <div\n style={styleBody}\n ref={this.bodyRef}\n className=\"dsg-Toggable-box__body\">\n\n {/* Contents of body */}\n {this.props.children.body}\n\n </div>\n </div>\n );\n }\n\n /**\n * Display appropriated icons in the head of the toggableBox\n */\n public displayIcons() {\n if (this.props.loading) {\n // Display loader\n return (\n <div className=\"dsg-loader-ring dsg-Toggable-box__loader\">\n <div></div>\n <div></div>\n <div></div>\n <div></div>\n </div>\n );\n } else if (this.props.hasError) {\n // Display error icon\n return (\n <i className=\"dsg-icon dsg-icon-warning dsg-Toggable-box__icon\"></i>\n );\n } else {\n // Display arrow\n return (\n <i\n onClick={this.handleClick}\n className=\"dsg-Toggable-box__arrow dsg-icon dsg-icon-play\">\n </i>\n );\n }\n }\n\n // --------------------------------- COMPONENT LIFECYCLE -------------------------------\n\n public componentDidMount() {\n /*\n Once the togglebox is rendered one time, we have the way of knowing the dynamic\n height of its contents. We force a second render to apply the right inline style\n values\n */\n if (isVisible(this.bodyRef.current)) { // If body isn't hidden\n this.setState({\n bodyHeightSet: true,\n });\n }\n\n window.addEventListener('resize', debounce(this.handleResizeEvent, 50));\n }\n\n public componentWillUnmount() {\n window.removeEventListener('resize', debounce(this.handleResizeEvent, 50));\n }\n\n public componentDidUpdate(prevProps, prevState) {\n /*\n As for the componentDidMount method, if one prop affecting the body size has\n been changed we update the state again\n */\n const bodyChange = prevProps.children.body !== this.props.children.body;\n const propToggledChange = (\n prevState.cachedToggled !== this.state.cachedToggled && // Prop toggled has changed\n prevState.toggled !== this.state.toggled // State toggled has switched\n );\n\n if (bodyChange || propToggledChange) {\n // Update bodysize if needed\n const bodyHeightSet = bodyChange ? isVisible(this.bodyRef.current) : null;\n\n // Call stop animation callback if toggled was changed from props\n const callback = propToggledChange ? this.stopAnimation : null;\n\n /*\n In case we change the state first, stopAnimation is passed as a callback.\n This way, we avoid changing the state again in a middle of the lifecycle.\n */\n if (bodyHeightSet) {\n this.setState({\n bodyHeightSet,\n }, callback);\n } else {\n this.stopAnimation();\n }\n }\n }\n\n // tslint:disable-next-line:member-ordering\n public static getDerivedStateFromProps(props: ToggableBoxProps, state: ToggableBoxState) {\n let {toggled, animationOn} = state;\n // Compare if toggled from props has changed\n if (state.cachedToggled !== props.toggled) {\n // We want to allow toggling from prop change\n toggled = props.toggled;\n animationOn = state.toggled !== props.toggled;\n }\n\n return {\n animationOn,\n cachedToggled: props.toggled,\n toggled,\n };\n }\n\n // --------------------------------- CUSTOM FUNCTIONS -------------------------------\n\n /**\n * Handle onClick event\n */\n public handleClick(e) {\n e.stopPropagation();\n this.props.onClick ? this.props.onClick(e) : this.toggleBody(e);\n }\n\n /**\n * Handle resize event\n */\n public handleResizeEvent() {\n // Force update to change the dynamic height of body\n this.forceUpdate();\n }\n\n /**\n * Toggle the 'toggled' state\n *\n * @param e Event Click event\n * @param callback () => void Callback triggered after state is set\n */\n public toggleBody(e?, callback?) {\n if (!this.props.children.body) {\n return;\n }\n\n this.setState({\n animationOn: true,\n toggled: !this.state.toggled,\n // Custom callback is for testing\n }, callback ? callback : this.stopAnimation);\n }\n\n /**\n * Disable animation state after waiting the appropriated time\n */\n public stopAnimation() {\n setTimeout(() => {\n this.setState({animationOn: false});\n }, this.props.animationDuration * 1000);\n }\n}\n"
},
{
"filePath":"/pwd/styleguide/source/js/components/ToggableBoxList/ToggableBoxList-data.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"\n/**\n * Author: D B\n * DateTime: 2019/9/10\n * Description: ToggableBoxList test and showcase datas\n * Will look for ids in the DOM and initialise the corresponding\n * react elements.\n */\n\nimport * as React from 'react';\nimport { isSmallScreen } from '../../dsg-util';\nimport { ToggableBoxList } from './ToggableBoxList';\n\n/**\n * Will return example data for a patient appointment toggable box\n *\n * @param options {} See below\n */\nconst patientAppointment = (options: {\n byDoctor?: boolean; // if cancelled, it will specify it has been by doctor or patient\n toggled?: boolean; // If true, the toggablebox will be open already\n type?: string; // can be 'confirmed', 'cancelled' or 'pending'. If undefined => past appointment.\n date?: string; // Date of the appointment\n drName?: string; // Dr name for this appointment\n speciality?: string; // Speciality name for this doctor.\n} = {}) => {\n\n const drName = options.drName || 'Dr. John Doe';\n const speciality = options.speciality || 'Dentist';\n let alert;\n let icon;\n let status;\n\n switch (options.type) {\n case 'pending':\n alert = 'info';\n icon = 'hourglass';\n status = 'This appointment is still pending confirmation from your practitioner.';\n break;\n case 'confirmed':\n alert = 'success';\n icon = 'tick';\n status = 'This appointment has been confirmed by your practitioner.';\n break;\n case 'cancelled':\n alert = 'error';\n icon = 'close';\n status = options.byDoctor ?\n 'This appointment has been cancelled by your practitioner.' :\n 'You have cancelled this appointment.';\n break;\n default:\n alert = 'muted';\n icon = 'history';\n break;\n }\n\n return {\n children: {\n head: (\n <div className=\"dsg-valign\">\n {icon ?\n <i className={\n 'dsg-icon dsg-icon-' + icon + ' dsg-semi-mg-right' +\n ' dsg-color--' + alert\n }></i> : null\n }\n <span className=\"dsg-hyphens dsg-flex-child-fix\">\n <strong>{options.date} - 12:50</strong> - {drName}\n </span>\n </div>\n ),\n // tslint:disable-next-line:object-literal-sort-keys\n body: (\n <div>\n\n {/* DOCTOR PRESENTATION */}\n <div className=\"dsg-team-member dsg-semi-mg-bottom dsg-valign\">\n <div className=\"dsg-profile-picture dsg-no-shrink\">\n <a href=\"#\">\n <img src=\"../../images/company_avatar.webp\"\n width=\"120\"\n height=\"120\"\n alt=\"avatar profile picture styleguide company\" />\n </a>\n </div>\n\n <div className=\"dsg-pd-left dsg-flex-child-fix\">\n <h2 className=\"dsg-team-member__title\">\n <a href=\"#\" className=\"dsg-no-decoration\">\n {drName}\n </a>\n </h2>\n\n <div className=\"dsg-semi-mg-top dsg-semi-mg-bottom\">\n <span className=\"dsg-team-member__label dsg-label--round dsg-label--default\">\n {speciality}\n </span>\n </div>\n\n <p className=\"dsg-team-member__text\">\n Other specialities : ORL, Surgeon, Ostheopath\n </p>\n </div>\n </div>\n\n {/* APPOINTMENT INFO */}\n <div className=\"dsg-table dsg-spaced-text\">\n <p className=\"dsg-table-row\">\n <span className=\"dsg-table-cell dsg-bold\">Patient</span>\n <span className=\"dsg-table-cell dsg-pd-left dsg-hyphens\">Bobby Joe</span>\n </p>\n <p className=\"dsg-table-row\">\n <span className=\"dsg-table-cell dsg-bold\">Date</span>\n <span className=\"dsg-table-cell dsg-pd-left dsg-hyphens\">{options.date}</span>\n </p>\n <p className=\"dsg-table-row\">\n <span className=\"dsg-table-cell dsg-bold\">Time</span>\n <span className=\"dsg-table-cell dsg-pd-left dsg-hyphens\">12:50</span>\n </p>\n <p className=\"dsg-table-row\">\n <span className=\"dsg-table-cell dsg-bold\">Address</span>\n <span className=\"dsg-table-cell dsg-pd-left dsg-hyphens\">\n Cabinet du {drName}<br/>\n 6 rue de Luxembourg<br/>\n 6798, Bertrange\n </span>\n </p>\n </div>\n\n {/* CALENDAR EVENT */}\n {options.type && options.type !== 'cancelled' ?\n <p className=\"dsg-semi-mg-top dsg-spaced-text\">\n <span className=\"dsg-bold\">Add this appointment to your calendar: </span>\n <a href=\"#\">Google</a> • \n <a href=\"#\">Other</a>\n </p>\n : null}\n\n { status ?\n <div className={'dsg-alert dsg-alert--' + alert + ' dsg-semi-mg-top'}>\n {status} <br/>\n {options.type === 'pending' ? <span><br/>\n You will receive notifications in case an earlier appointment becomes available\n with this practitioner. <a href=\"#\">Unsubscribe from the waiting list</a> to\n stop receiving those notifications.\n </span> : null }\n </div>\n : null}\n\n { options.type ? <div className=\"dsg-col--xs dsg-semi-mg-top\">\n {options.type === 'pending' || options.type === 'confirmed' || options.type === 'cancelled' ?\n <button className=\"mdc-button mdc-button--raised mdc-button--info dsg-semi-mg-bottom\">\n <strong className=\"mdc-button__label\">Reschedule</strong>\n </button>\n : null}\n {options.type === 'pending' || options.type === 'confirmed' ?\n <span className=\"dsg-desktop-mg\"></span>\n : null}\n {options.type === 'pending' || options.type === 'confirmed' ?\n <button className=\"mdc-button mdc-button--raised mdc-button--error dsg-semi-mg-bottom\">\n <strong className=\"mdc-button__label\">Cancel</strong>\n </button>\n : null}\n </div> : null }\n\n {options.type ? <div className=\"dsg-anti-mg-bottom\"></div> : null}\n </div>\n ),\n },\n className: 'dsg-Toggable-box--neutral',\n toggled: !!options.toggled,\n };\n};\n\nexport const toggableBoxLists = [\n {\n reactElement : (\n <ToggableBoxList\n activeBoxesDefault={[0]}\n autoClose={true}\n boxes={[\n {\n children: {\n body: 'Content 1',\n head: 'Item 1',\n },\n },\n {\n children: {\n body: 'Content 2',\n head: 'Item 2',\n },\n },\n {\n children: {\n body: 'Content 3',\n head: 'Item 3',\n },\n },\n ]}\n />\n ),\n rootId: 'toggable-box-list',\n },\n {\n reactElement : (\n <ToggableBoxList\n activeBoxes={[0]}\n boxes={[\n {\n children: {\n body: 'Content 1',\n head: 'Item 1',\n },\n },\n {\n children: {\n body: 'Content 2',\n head: 'Item 2',\n },\n },\n {\n children: {\n body: 'Content 3',\n head: 'Item 3',\n },\n },\n ]}\n />\n ),\n rootId: 'toggable-box-list-no-autoclose',\n },\n {\n reactElement : (\n <ToggableBoxList\n activeBoxes={[0]}\n autoClose={isSmallScreen}\n boxes={[\n patientAppointment({\n date: '03/02/21',\n toggled: true,\n type: 'confirmed',\n }),\n patientAppointment({\n date: '08/02/21',\n drName: 'Hubert Blaine Wolfeschlegelsteinhausenbergerdorff Sr.',\n speciality: 'Functional reeducation and rehabilitation',\n type: 'pending',\n }),\n patientAppointment({\n byDoctor: true,\n date: '12/02/21',\n type: 'cancelled',\n }),\n patientAppointment({\n byDoctor: false,\n date: '13/02/21',\n type: 'cancelled',\n }),\n ]}\n />\n ),\n rootId: 'patient-appointments-upcoming',\n },\n {\n reactElement : (\n <ToggableBoxList\n autoClose={isSmallScreen}\n boxes={[\n patientAppointment({\n date: '25/01/21',\n }),\n patientAppointment({\n date: '21/01/21',\n }),\n ]}\n />\n ),\n rootId: 'patient-appointments-history',\n },\n];\n"
},
{
"filePath":"/pwd/styleguide/source/js/components/ToggableBoxList/ToggableBoxList.test.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"\n/**\n * Author: D B\n * DateTime: 2019/9/10\n * Description: Tests for ToggableBoxList component\n */\n\nimport { mount, render, shallow } from 'enzyme';\nimport * as React from 'react';\nimport {\n getComponents,\n simpleSnapshots,\n} from '../../dsg-test-util';\nimport { ToggableBoxList } from './ToggableBoxList';\nimport { toggableBoxLists } from './ToggableBoxList-data';\n\nconst components = getComponents(toggableBoxLists, [\n 'toggable-box-list',\n 'toggable-box-list-no-autoclose',\n]);\n\nsimpleSnapshots(components);\n\n// Test toggle open with autoclose on\ntest('Test autoclose', () => {\n const component = shallow(components[0].reactElement); // Autoclose on\n const instance: ToggableBoxList = component.instance() as ToggableBoxList;\n expect(instance.state.activeBoxes[0]).toEqual(0);\n // Simulate toggle open\n instance.toggleBox(1, () => {\n expect(instance.state.activeBoxes[0]).toEqual(1);\n expect(component).toMatchSnapshot();\n });\n});\n\n// Test toggle open with autoclose off\ntest('Test no autoclose', () => {\n const component = shallow(components[1].reactElement); // Autoclose off\n const instance: ToggableBoxList = component.instance() as ToggableBoxList;\n expect(instance.state.activeBoxes[0]).toEqual(0);\n\n // Simulate toggle open\n instance.toggleBox(1, () => {\n expect(instance.state.activeBoxes.length).toEqual(2);\n expect(component).toMatchSnapshot();\n\n // Toggle close\n instance.toggleBox(1, () => {\n expect(instance.state.activeBoxes.length).toEqual(1);\n expect(component).toMatchSnapshot();\n });\n });\n});\n"
},
{
"filePath":"/pwd/styleguide/source/js/components/ToggableBoxList/ToggableBoxList.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"\n/**\n * Author: D B\n * DateTime: 2019/9/10\n * Description: ToggableBoxList React component\n * This React component is used for behaviour that could be common and widely\n * used accross different lists of toggablebox.\n */\n\nimport * as React from 'react';\nimport { isFunction } from '../../dsg-util';\n\nimport {\n ToggableBox,\n ToggableBoxProps,\n} from '../ToggableBox/ToggableBox';\n\nexport interface ToggableBoxListProps {\n /** Put in this array the toggableBox indexes that should be toggled. */\n activeBoxes?: number[];\n /** Put in this array the toggableBox indexes that should be toggled on first state. */\n activeBoxesDefault?: number[];\n /** If true (or if it returns true) the opening of a toggableBox will close any other opened box. */\n autoClose?: boolean | (() => boolean);\n /** Array of ToggableBoxesProps. Each item corresponds to a ToggableBox. */\n boxes: ToggableBoxProps[];\n /** CSS class attribute to apply to the container div */\n className?: string;\n /** Function that will be called when clicking on the head of one of the ToggableBoxes. */\n onHeadClick?: (e: Event, boxIndex: number) => void;\n /**\n * If true, the stack order of each element will be reversed (meaning the first element of the list\n * will technically be above the next one. It can help in certain cases, when a tooltip from an item is getting\n * hidden by the following item)\n */\n reverseStackOrder?: boolean;\n}\n\nexport interface ToggableBoxListState {\n /** This array contains the toggableBox indexes that should be toggled. */\n activeBoxes: number[];\n activeBoxesCached: number[];\n}\n\nexport class ToggableBoxList extends React.Component<\n ToggableBoxListProps,\n ToggableBoxListState\n> {\n\n public static defaultProps: Partial<ToggableBoxListProps> = {\n activeBoxes: [],\n autoClose: false,\n boxes: [],\n className: '',\n };\n\n constructor(props: ToggableBoxListProps) {\n super(props);\n\n this.displayToggableBox = this.displayToggableBox.bind(this);\n this.toggleBox = this.toggleBox.bind(this);\n\n this.state = {\n activeBoxes: props.activeBoxesDefault || props.activeBoxes,\n activeBoxesCached: props.activeBoxes,\n };\n }\n\n // --------------------------------- RENDER -------------------------------\n\n public render() {\n const boxes = [...this.props.boxes];\n let className = this.props.className;\n if (this.props.reverseStackOrder) {\n boxes.reverse();\n className = 'dsg-col-reverse ' + className;\n }\n\n return (\n <div className={className}>\n {boxes.map(this.displayToggableBox)}\n </div>\n );\n }\n\n /**\n * Display a single ToggableBox, that will have its props overriden by the\n * current state of the ToggableBoxList.\n *\n * @param box ToggableBoxProps ToggableBox to display\n * @param i number ToggableBox index (from the boxes prop array)\n */\n public displayToggableBox(box: ToggableBoxProps, i: number) {\n\n // We define here which props will be overriden by the list\n const overrideProps = {\n /*\n We don't want toggableBox to be able to work individually, so we override their\n toggle/handleClick function\n */\n onClick: (e) => this.handleClickOnToggableBox(e, i),\n toggled: this.state.activeBoxes.indexOf(i) !== -1,\n } as ToggableBoxProps;\n\n return (\n <div className=\"dsg-semi-mg-bottom\" key={'toggable-box-list-item-' + i} >\n <ToggableBox {...box} {...overrideProps} />\n </div>\n );\n }\n\n // --------------------------------- COMPONENT LIFECYCLE -------------------------------\n\n // tslint:disable-next-line:member-ordering\n public static getDerivedStateFromProps(props: ToggableBoxListProps, state: ToggableBoxListState) {\n let {activeBoxes} = state;\n\n // Compare if active boxes from props has changed\n if (state.activeBoxesCached !== props.activeBoxes) {\n // We want to allow toggling from prop change\n activeBoxes = props.activeBoxes;\n }\n\n return {\n activeBoxes,\n activeBoxesCached: props.activeBoxes,\n };\n }\n\n // --------------------------------- CUSTOM FUNCTIONS -------------------------------\n\n /**\n * Event handler called when one of the ToggableBox's head is clicked\n *\n * @param e Event Click event\n * @param boxIndex number ToggableBox index (from the boxes prop array)\n */\n public handleClickOnToggableBox(e: Event, boxIndex: number) {\n if (this.props.onHeadClick) {\n this.props.onHeadClick(e, boxIndex);\n } else {\n this.toggleBox(boxIndex);\n }\n }\n\n /**\n * Toggle a box in the list. If the autoClose prop is set to true,\n * opening one box will close the others.\n *\n * @param boxIndex number ToggableBox index (from the boxes prop array)\n * @param callback () => void Callback function called after the setState function\n */\n public toggleBox(boxIndex: number, callback?: () => void) {\n\n const autoClose = isFunction(this.props.autoClose) ?\n (this.props.autoClose as () => boolean)() :\n this.props.autoClose;\n const activeBoxes = autoClose ? [] : [...this.state.activeBoxes];\n\n const position = this.state.activeBoxes.indexOf(boxIndex);\n\n // If the box is closed, we open the box\n if (position === -1) {\n activeBoxes.push(boxIndex);\n\n // If the box is toggled (and autoClose is off), we close only this box\n } else if (!autoClose) {\n activeBoxes.splice(position, 1);\n }\n\n this.setState({activeBoxes}, callback ? callback : null);\n }\n}\n"
},
{
"filePath":"/pwd/styleguide/source/js/datas/DatePicker-data.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"/**\n * Author: D B\n * DateTime: 18/09/26\n * LastUpdated: 18/09/26\n * Description: Example datas for color-picker\n */\n\nimport * as React from 'react';\nimport DatePicker from 'react-datepicker';\n\nexport const datePickers = [\n {\n reactElement : (\n <DatePicker inline />\n ),\n rootId: 'datepicker-default',\n },\n];\n"
},
{
"filePath":"/pwd/styleguide/source/js/datas/slots.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"\n/**\n * Author: D B\n * DateTime: 18/10/05\n * LastUpdated: 18/10/15\n * Description:\n * \tSlots hardcoded data for the calendar examples.\n * \tTODO : Test with classes\n * TODO : Test with end date in another day (error)\n */\n\nexport default {\n 4101750000 : [\n {\n start: 4101782400,\n },\n {\n end: 4101796800,\n legend: 'Walkin',\n start: 4101786000, // Test long slot\n },\n {\n end: 4101794800,\n start: 4101786000, // Test order long slot\n },\n {\n end: 4101794800,\n start: 4101786000, // Test duplicate long slot\n },\n {\n start: 4101792400,\n },\n ],\n 4101836400 : [\n {\n start: 4101868800,\n },\n {\n start: 4101865200, // Test order\n },\n {\n start: 4101865200, // Test duplicate slot\n },\n {\n start: 4101898800,\n },\n {\n start: 4101865200, // Test several duplicates\n },\n {\n start: 4101905200,\n },\n {\n start: 4101909200,\n },\n {\n start: 4101918200,\n },\n ],\n 4101922800 : [], // Test empty date\n 4102009200 : [ // Test date with slots from the wrong day\n {\n start: 980499600, // This not the same day at all\n },\n ],\n 101588760 : [], // Test Past empty date\n 980463600 : [ // Test Past date with slot\n {\n start: 980499600,\n },\n ],\n};\n"
},
{
"filePath":"/pwd/styleguide/source/js/dsg-test-util.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"\n/**\n * Author: D B\n * DateTime: 19/06/07\n * Description: Set of helper functions for jest tests\n */\n\nimport { shallow } from 'enzyme';\n\n/**\n * Collect components that we want to test\n *\n * @param components: object[]\n * @param ids: string[]\n */\nexport const getComponents = (components, ids) => {\n return ids.map((id) => {\n return components.find((component) => component.rootId === id);\n });\n};\n\n/**\n * Get a simple snapshot of every type of box\n *\n * @param components: object[]\n */\nexport const simpleSnapshots = (components) => {\n components.forEach((component) => {\n test(component.rootId, () => {\n const reactComponent = shallow(component.reactElement);\n expect(reactComponent).toMatchSnapshot();\n });\n });\n};\n"
},
{
"filePath":"/pwd/styleguide/source/js/dsg-util.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"/**\n * Author: D B\n * DateTime: 18/09/21\n * Description: Helper functions and variables, made for the styleguide\n * This file is structured in the following way :\n * - Test methods : Always return true or false (variables types, states...)\n * - String methods\n * - Function methods\n * - Array methods\n * - Dom methods (elements manipulation)\n */\n\n// ******************** COMPLEX VARIABLES ***********************\n\nexport const urlRegex = /((https?:\\/\\/)|(www\\.))[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_\\+.~#?&//=]*)/;\n\n// ******************** TEST METHODS ***********************\n\n// Returns true if variable is...\nexport const isNumber = x => !isNaN(parseFloat(x)) && isFinite(x);\nexport const isUndefined = x => typeof x === 'undefined';\nexport const isFunction = x => x && {}.toString.call(x) === '[object Function]';\nexport const isArray = x => Array.isArray(x);\nexport const isEmpty = x => !Array.isArray(x) || !x.length;\nexport const isEmptyObj = x => Object.keys(x).length === 0 && x.constructor === Object;\n/** Count as empty: if there are only whitespaces */\nexport const isEmptyString = (str: string) => !/^\\s*$/.test(str);\n\n/** Returns true is screen is small (possible mobile device) */\nexport const isSmallScreen = () => window.innerWidth < 768;\n\n/**\n * Returns true if HTMLElement is visible (and not fixed)\n *\n * @param el\n */\nexport const isVisible = (el: HTMLElement): boolean => {\n return el && el.offsetParent !== null;\n};\n\n/**\n * Returns the correct charCode of a keypress event\n *\n * @param e: Event\n */\nexport const getCharCode = (e): number => {\n return typeof e.which === 'number' ? e.which : e.keyCode;\n};\n\n// ******************** STRING METHODS ***********************\n\n/**\n * Takes as arg an object where keys are class names and values are booleans.\n * Returns a string of all classes names which values are true, separated by spaces.\n *\n * @param classObject\n */\nexport const setCSSClasses = (classObject: object): string => {\n let classString = '';\n for (const className in classObject) {\n if (classObject.hasOwnProperty(className)) {\n classString += classObject[className] ? (classString ? ' ' : '') + className : '';\n }\n }\n return classString;\n};\n\n/**\n * Put first letter of a string to uppercase\n *\n * @param str: string\n */\n// export const capitalize = (str: string): string => {\n// return str[0].toUpperCase() + str.slice(1);\n// };\n\n// ******************** FUNCTION METHODS ***********************\n\n/**\n * As long as the debounce method is called, it will delay the func by the specified\n * amount of time.\n *\n * @param func\n * @param delay\n */\nexport const debounce = (func, delay) => {\n let inDebounce;\n return function() {\n const context = this;\n const args = arguments;\n clearTimeout(inDebounce);\n inDebounce = setTimeout(() => func.apply(context, args), delay);\n };\n};\n\n/**\n * Call the function attached one time, and prevent it from being recalled\n * until the specified amount of time\n *\n * @param func\n * @param limit\n */\nexport const throttle = (func, limit) => {\n let inThrottle;\n return function() {\n const args = arguments;\n const context = this;\n if (!inThrottle) {\n func.apply(context, args);\n inThrottle = true;\n setTimeout(() => inThrottle = false, limit);\n }\n };\n};\n\n// ******************** ARRAY METHODS ***********************\n\n/** Used as references for various `Number` constants. */\nconst MAX_SAFE_INTEGER = 9007199254740991;\n\n/** Used as references for the maximum length and index of an array. */\nconst MAX_ARRAY_LENGTH = 4294967295;\n\n/**\n * Invokes the iteratee `n` times, returning an array of the results of\n * each invocation. The iteratee is invoked with one argumentindex).\n *\n * @param {number} n The number of times to invoke `iteratee`.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array} Returns the array of results.\n */\nexport const times = (n: number, iteratee: (index: number) => any) => {\n if (n < 1 || n > MAX_SAFE_INTEGER) {\n return [];\n }\n let index = -1;\n const length = Math.min(n, MAX_ARRAY_LENGTH);\n const result = new Array(length);\n while (++index < length) {\n result[index] = iteratee(index);\n }\n index = MAX_ARRAY_LENGTH;\n n -= MAX_ARRAY_LENGTH;\n while (++index < n) {\n iteratee(index);\n }\n return result;\n};\n\n/**\n * Returns member of array by testing each member with a function\n *\n * @param array\n * @param testMethod\n */\nexport const findInArray = (array: any[], testMethod: (val, i?: number) => boolean): number => {\n let found = null;\n let i = 0;\n\n for (const val of array) {\n if (testMethod(val, i)) {\n found = val;\n break;\n }\n i++;\n }\n\n return found ? i : -1;\n};\n\n// ******************** DOM METHODS ***********************\n\n/**\n * Return how far from the top of the page we scrolled\n */\nexport const getScrollOffset = (): number => {\n let yScroll;\n\n if (window.pageYOffset) {\n yScroll = window.pageYOffset;\n } else if (document.documentElement && document.documentElement.scrollTop) {\n yScroll = document.documentElement.scrollTop;\n } else if (document.body) {\n yScroll = document.body.scrollTop;\n }\n\n return yScroll;\n};\n\n/**\n * Return all elements from the DOM with a fixed position\n */\nexport const getAllFixedElements = (): HTMLElement[] => {\n const elems = getArrayByClassName('*');\n\n return elems.filter((elem: HTMLElement) => {\n return window.getComputedStyle(elem).getPropertyValue('position') === 'fixed';\n });\n};\n\n/**\n * Get elements by className and transform the resulting HTMLCollection into\n * an Array of HTMLElements, allowing use of Array native functions. This\n * way is IE11 compatible, contrary to the use of Array.from() function.\n *\n * @param className : string\n */\nexport const getArrayByClassName = (className: string): HTMLElement[] => {\n return Array.prototype.slice.call(document.getElementsByClassName(className));\n};\n\n/**\n * Pass an HTML string and returns the according ChildNode\n *\n * @param htmlString : string\n */\nexport const createElementFromHTML = (htmlString: string): ChildNode => {\n const div = document.createElement('div');\n div.innerHTML = htmlString.trim();\n return div.firstChild;\n};\n\n/**\n * Dispatch an event in a cross-browser compatible way (IE11 compatible)\n *\n * @param eventName : string\n */\nexport const cleanDispatchEvent = (eventName) => {\n const event = document.createEvent('Event');\n event.initEvent(eventName, false, true);\n window.dispatchEvent(event);\n};\n\n/**\n * Return scrollbar size. Will return vertical scrollbar's width if the side\n * argument is 'width' or the horizontal scrollbar's height if the argument is 'height'\n *\n * @param side: string\n * @return number\n */\n// const scrollbarSize = {};\n// export const getScrollbarSize = (side: string = 'width') => {\n\n// // The scrollbar\n// if (scrollbarSize[side]) {\n// return scrollbarSize[side];\n// }\n\n// const outer = document.createElement('div');\n// outer.style.visibility = 'hidden';\n// outer.style[side] = '100px';\n// outer.style.msOverflowStyle = 'scrollbar'; // needed for WinJS apps\n\n// document.body.appendChild(outer);\n\n// const sideNoScroll = outer['offset' + capitalize(side)];\n// // force scrollbars\n// outer.style.overflow = 'scroll';\n\n// // add innerdiv\n// const inner = document.createElement('div');\n// inner.style[side] = '100%';\n// outer.appendChild(inner);\n\n// const sideWithScroll = inner['offset' + capitalize(side)];\n\n// // remove divs\n// outer.parentNode.removeChild(outer);\n// scrollbarSize[side] = sideNoScroll - sideWithScroll;\n\n// return scrollbarSize[side];\n// };\n\n/**\n * Return true if a scrollbar if visible. By default will return the status of the\n * vertical scrollbar (or if the side argument is 'height'), but if the side argument\n * is 'width', it will return the horizontal scrollbar\n *\n * @param target: HTMLElement\n * @param side: string\n * @return boolean\n */\n// export const isScrollbarVisible = (target: HTMLElement = document.body, side: string = 'height') => {\n\n// // Size of target if it could fit fully on the screen\n// const scrollSize = target['scroll' + capitalize(side)];\n// // Size of the element\n// const actualSize = target['client' + capitalize(side)];\n\n// return ( scrollSize > actualSize || (\n// target === document.body &&\n// scrollSize > window['inner' + capitalize(side)]\n// )\n// );\n// };\n\n/**\n * Animate the window with a smooth scrolling to the value\n * indicated\n *\n * @param target : Number value or DOM element\n * @param time : Duration of the animation\n */\nexport const smoothScrollTo = (target, time = 1000): void => {\n // Get position of target\n const targetOffset = isNumber(target) ? target : target.offsetTop;\n // Current scroll value of the user on the page\n const currentPosition = getScrollOffset();\n // Max scroll value on page\n const maxScroll = document.body.scrollHeight;\n // Height of screen\n const windowHeight = window.innerHeight;\n\n /*\n We calculate the scroll distance to get to the top of the target element.\n Scroll distance is limited to the page bottom\n */\n const scrollDistance =\n // If getting to the top of the target element gets us below the bottom of page...\n targetOffset + windowHeight > maxScroll ?\n // ... we instead stop just above the bottom of page, by using the window's height\n maxScroll - windowHeight - currentPosition :\n targetOffset - currentPosition;\n\n /*\n We want to animate the body and every fixed elements at the same time.\n We do this because when a element get transformed with the translate attribute,\n all of its fixed children will become static. So we have to animate them as well.\n */\n const elementsToTransform = [document.body].concat(getAllFixedElements());\n\n // We will need top values of every fixed element to reposition them during the animation.\n const originalTops = elementsToTransform.map(el => window.getComputedStyle(el).top);\n\n // Store old style inline to reset them after the animation\n const oldStyles = {};\n const attributes = ['top', 'bottom', 'position'];\n attributes.forEach(attr => oldStyles[attr] = []);\n\n // We animate them by manipulating transition and transform CSS properties\n elementsToTransform.forEach((element: HTMLElement, i) => {\n\n /*\n Before any animation we set the initial position of transform values :\n - Body should be set on 0 -> Fixed elements are now static\n - Fixed divs should be set to page scroll offset, and positionned absolute\n -> simulate fixed behaviour\n */\n const initTransformValue = 'translate(0, ' + (i ? currentPosition : 0) + 'px)';\n element.style.webkitTransform = initTransformValue;\n element.style.transform = initTransformValue;\n // We prevent any transition (especially on top or bottom) from happening\n element.style.webkitTransition = 'none';\n element.style.transition = 'none';\n\n if (i) {\n attributes.forEach(attr => oldStyles[attr][i] = element.style[attr]);\n element.style.top = originalTops[i];\n element.style.position = 'absolute';\n // We don't want any bottom attribute to have an impact on the position of element\n element.style.bottom = 'auto';\n }\n\n /*\n Here we animate all contents : body and fixed divs.\n We set a timeout so the init transform values are applied to the DOM before the\n following changes.\n */\n window.setTimeout(() => {\n const transitionValue = 'transform ease ' + time / 1000 + 's';\n const transformValue = 'translate(0, ' +\n // The scroll distance must be negative for body value, but positive for fixed elements\n (\n scrollDistance * (i ? 1 : -1) +\n // we add the page scroll offset value too for fixed divs\n (i ? currentPosition : 0)\n ) + 'px)';\n element.style.webkitTransition = transitionValue;\n element.style.transition = transitionValue;\n element.style.webkitTransform = transformValue;\n element.style.transform = transformValue;\n }, 0);\n });\n\n // After the transition is done, we remove any transform and transition values\n window.setTimeout(() => {\n elementsToTransform.forEach((element: HTMLElement, i) => {\n /*\n transition is set to none so the reset of new top and bottom value\n doesn't trigger a new animation\n */\n element.style.webkitTransition = 'none';\n element.style.transition = 'none';\n element.style.webkitTransform = '';\n element.style.transform = '';\n\n // Turn back fixed elements into their original position\n if (i) {\n attributes.forEach(attr => element.style[attr] = oldStyles[attr][i]);\n }\n });\n\n /*\n Scrolling until now was simulated with a transform effect. This is where\n we scroll for real : at the same time we cancel the transform attribute.\n */\n window.scrollTo(0, targetOffset);\n cleanDispatchEvent('dsg-endSmoothScrolling');\n\n }, time);\n\n /*\n After the end of smooth scrolling, we finally stop overriding transitions\n */\n window.addEventListener('dsg-endSmoothScrolling', () => {\n elementsToTransform.forEach((element: HTMLElement, i) => {\n element.style.webkitTransition = '';\n element.style.transition = '';\n });\n });\n};\n"
},
{
"filePath":"/pwd/styleguide/source/js/main-all.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"\n/**\n * Author: D B\n * DateTime: 18/10/24\n * Description: Entry point of bundle for styleguide.\n */\n\nimport * as dsgUtils from './dsg-util';\nimport { initMdc } from './mdc-init';\nimport { initReactComponents } from './react-init';\nimport { initScripts } from './scripts-init';\n\n// Making the utils functions global so we can use them in any script\n(window as any).dsgUtils = dsgUtils;\n(global as any).dsgUtils = dsgUtils;\n\n// Initiate all scripts not using framework libs like react\ninitScripts();\n\n// Initiate Material stuff\ninitMdc();\n\n// Initiate all react components : Inject components inside HTML root elements\ninitReactComponents();\n"
},
{
"filePath":"/pwd/styleguide/source/js/main-cpp.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"\n/**\n * Author: D B\n * DateTime: 18/10/24\n * Description: Entry point of bundle for Central Patient Portal.\n */\n\nimport * as dsgUtils from './dsg-util';\nimport { initMdc } from './mdc-init';\nimport { initScripts } from './scripts-init';\n\n// Making the utils functions global so we can use them in any script\n(window as any).dsgUtils = dsgUtils;\n(global as any).dsgUtils = dsgUtils;\n\n// Initiate all scripts not using framework libs like react\ninitScripts();\n\n// Initiate Material stuff\ninitMdc();\n"
},
{
"filePath":"/pwd/styleguide/source/js/main-pro.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"\n/**\n * Author: D B\n * DateTime: 18/10/24\n * Description: Entry point of bundle for pro.\n */\n\nimport initCssVars from 'css-vars-ponyfill';\nimport * as dsgUtils from './dsg-util';\n\n// Making the utils functions global so we can use them in any script\n(window as any).dsgUtils = dsgUtils;\n(global as any).dsgUtils = dsgUtils;\n\n// Initiate CSS variables ponyfill\ninitCssVars();\n"
},
{
"filePath":"/pwd/styleguide/source/js/main-rwm.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"\n/**\n * Author: D B\n * DateTime: 19/01/17\n * Description: Entry point of bundle for Ratings-badge (RWM).\n */\n\nimport { initStarRaters } from './scripts/star-rater';\n\ninitStarRaters();\n"
},
{
"filePath":"/pwd/styleguide/source/js/mdc-init.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"\n/**\n * Author: D B\n * DateTime: 19/03/07\n * Description: Initiate Material components scripts\n */\n\nimport {MDCRipple} from '@material/ripple';\nimport {MDCSelect} from '@material/select';\nimport {MDCSelectIcon} from '@material/select/icon';\nimport {MDCTextField} from '@material/textfield/index.js';\n\nexport const initMdc = () => {\n\n // Initiate ripple effect\n const rippleSelector = '.mdc-button, .mdc-icon-button , .mdc-card__primary-action';\n const ripples = [].map.call(document.querySelectorAll(rippleSelector), (el) => {\n const ripple = new MDCRipple(el);\n ripple.unbounded = true;\n return ripple;\n });\n\n // Initiate text-fields\n const mdcTextFields = [].map.call(document.querySelectorAll('.mdc-text-field'), (el) => {\n return new MDCTextField(el);\n });\n\n // Initiate select inputs\n const mdcSelects = [].map.call(document.querySelectorAll('.mdc-select'), (el) => {\n return new MDCSelect(el);\n });\n\n // Initiate select-icons\n const mdcSelectIcons = [].map.call(document.querySelectorAll('.mdc-select__icon'), (el) => {\n return new MDCSelectIcon(el);\n });\n\n};\n"
},
{
"filePath":"/pwd/styleguide/source/js/models/Slot.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"import * as moment from 'moment-timezone';\n\nexport default interface Slot {\n start: number;\n end?: number;\n classNames?: string[];\n legend?: string;\n\n // Internal to Calendar\n hasDuplicate?: boolean;\n hidden?: boolean;\n space?: number;\n\n // User custom\n data?: any;\n}\n"
},
{
"filePath":"/pwd/styleguide/source/js/react-init.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"/**\n * Author: D B\n * DateTime: 18/09/21\n * Description:\n * Will look for ids in the DOM and initialise the corresponding\n * react elements.\n * IMPORTANT : If you change anything to this script, check that the new-component\n * helper script is still working => /scripts/new-component.js\n */\n\n/* tslint:disable:ordered-imports */\n\nimport * as React from 'react';\nimport * as ReactDOM from 'react-dom';\n\n// **** @DATAS to input in root elements ****\nimport { buttonTabsMenus } from './components/ButtonTabsMenu/ButtonTabsMenu-data';\nimport { calendars } from './components/Calendar/Calendar-data';\nimport { characterPickers } from './components/CharacterPicker/CharacterPicker-data';\nimport { colorPickers } from './components/ColorPicker/ColorPicker-data';\nimport { datePickers } from './datas/DatePicker-data';\nimport { loaders } from './components/Loader/Loader-data';\nimport { tabTextareas } from './components/TabTextarea/TabTextarea-data';\nimport { smartModals } from './components/SmartModal/SmartModal-data';\nimport { toggableBoxLists } from './components/ToggableBoxList/ToggableBoxList-data';\nimport { toggableBoxes } from './components/ToggableBox/ToggableBox-data';\n// **** /DATAS ****\n\n/*\n Components in the list of components must comply to this interface\n 1 : id of the root element. React component will be initialised here\n 2 : ReactElement that we want to initialise. Props are attribute of the element.\n*/\ninterface ComponentInitInfos {\n rootId: string /* 1 */;\n reactElement: React.ReactElement<any> /* 2 */;\n}\n\n// List of components to initialise\nconst components: ComponentInitInfos[] = [].concat(\n buttonTabsMenus,\n calendars,\n characterPickers,\n colorPickers,\n datePickers,\n loaders,\n tabTextareas,\n smartModals,\n toggableBoxLists,\n toggableBoxes,\n);\n\n/**\n * Search for ids of each item of the components list. If it finds them, it will\n * initiate the React Components by using ReactDOM.render method\n */\nexport const initReactComponents = () => {\n components.forEach((component: ComponentInitInfos) => {\n const rootEl = document.getElementById(component.rootId);\n\n if (rootEl) {\n ReactDOM.render(component.reactElement, rootEl);\n }\n });\n};\n"
},
{
"filePath":"/pwd/styleguide/source/js/scripts/scroll-menu.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"\n/**\n * Author: D B\n * DateTime: 18/11/07\n * LastUpdated: 19/01/07\n * Description: Handle behaviour of tabs menu :\n * - Animation of line below different tabs : on hover, resize, on scroll\n * - Smooth scroll to hash target on click\n */\n\nimport {\n debounce,\n getArrayByClassName,\n getScrollOffset,\n isEmpty,\n isUndefined,\n smoothScrollTo,\n} from '../dsg-util';\n\nconst line = document.getElementById('dsg-scroll-menu-line');\nconst tabs = getArrayByClassName('dsg-scroll-menu__item');\nlet currentTab;\nlet scrolling = false;\n\n// Change this value to change the length of the smooth scrolling\nconst animationDuration = 1000;\n\n/**\n * Init tabs menu script\n */\nexport const initTabsMenu = () => {\n\n if (isEmpty(tabs) || !line) {\n return;\n }\n\n selectTab(tabs[0]);\n\n // Add event listener to menu items\n tabs.forEach((tab: HTMLElement) => {\n\n // Line will get below hovered item\n tab.onmouseenter = () => moveLineToTab(tab);\n\n // Line will get back to selected item when hovering stopped\n tab.onmouseleave = () => moveLineToTab(currentTab);\n\n // On click on link, the selected item will change\n const link = tab.children[0] as HTMLElement;\n link.onclick = (e) => handleTabClick(e, tab);\n });\n\n // On resize and scroll, we want to check if the currentTab hasn't change\n window.addEventListener('scroll', detectCurrentTab);\n // Debounce function is added here because we need less precision\n window.addEventListener('resize', debounce(() => {\n detectCurrentTab();\n }, 50));\n // We can also trigger it manually with a custom event\n window.addEventListener('dsg-reset-scroll-menu', detectCurrentTab);\n};\n\n/**\n * Handle behaviour of tabs menu when mouse click on a tab\n */\nconst handleTabClick = (e, tab: HTMLElement): void => {\n e.preventDefault();\n\n // If still scrolling from last click on tab, we don't allow a new one.\n if (scrolling) {\n return;\n }\n\n selectTab(tab);\n\n // If tab has a hash target, we will smooth scroll to it\n const linkTarget = document.getElementById(e.target.hash.substr(1));\n if (linkTarget) {\n smoothScrollTo(linkTarget);\n } else {\n return;\n }\n\n // Switch the scrolling variable to true for duration of scrolling\n scrolling = true;\n window.setTimeout(() => {\n scrolling = false;\n }, animationDuration);\n};\n\n/**\n * Position and resize line relative to the targeted tab\n */\nconst moveLineToTab = (tab: HTMLElement): void => {\n line.style.left = tab.offsetLeft + 'px';\n line.style.width = tab.offsetWidth + 'px';\n};\n\n/**\n * Compare page scroll offset compared with tabs targets position, and\n * select accordingly the right tab as the currentTab\n */\nconst detectCurrentTab = (e?): void => {\n const offsets = getTargetOffsets();\n const currentPosition = getScrollOffset();\n const maxScrollOffset = document.body.scrollHeight - window.innerHeight;\n\n // If we are above the first offset, the first tab is selected\n if (currentPosition < offsets[0]) {\n selectTab(tabs[0]);\n\n // If we are at the end of the document, the last tab is selected\n } else if (currentPosition === maxScrollOffset) {\n selectTab(tabs[tabs.length - 1]);\n\n // Otherwise we watch from which target we are the closest\n } else {\n offsets.forEach((offset, i) => {\n\n // If scroll position is below the target position...\n if (currentPosition >= offset && (\n // ...and above the next target's position...\n currentPosition < offsets[i + 1] ||\n // ...or there is no other target after\n isUndefined(offsets[i + 1])\n )) {\n selectTab(tabs[i]);\n }\n });\n }\n};\n\n/**\n * Return an array of items corresponding to targets id and offsetScroll values\n */\nconst getTargetOffsets = (): any[] => {\n return tabs.map((tab) => {\n const link = tab.children[0] as HTMLLinkElement;\n const id = link.href.split('#')[1];\n const element = document.getElementById(id) as HTMLElement;\n\n return element ? element.offsetTop : null;\n }).filter(tab => tab !== null);\n};\n\n/**\n * Change selected tab and move line below selected Tab\n */\nconst selectTab = (tab: HTMLElement): void => {\n currentTab = tab;\n moveLineToTab(tab);\n};\n"
},
{
"filePath":"/pwd/styleguide/source/js/scripts/star-picker.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"\n/**\n * Author: D B\n * DateTime: 19/01/17\n * LastUpdated: 19/01/17\n * Description: Construct and handle behaviour of star-pickers (ratings)\n */\n\nimport {\n cleanDispatchEvent,\n getArrayByClassName,\n} from '../dsg-util';\nimport { buildStar } from './star-rater';\n\n/**\n * First build and listening to events to reset builds\n */\nexport const initStarPickers = () => {\n buildStarPickers();\n window.addEventListener('dsg-reset-star-raters', buildStarPickers);\n};\n\n/**\n * Search for root elements to build the star raters\n */\nconst buildStarPickers = () => {\n const starPickers = getArrayByClassName('dsg-star-picker');\n starPickers.forEach(buildStarPicker);\n};\n\n/**\n * Builds a star picker (an input for picking a certain number of stars)\n *\n * @param starPicker : HTMLElement\n */\nconst buildStarPicker = (starPicker: HTMLElement) => {\n // We create the five gray stars\n for (let i = 0; i < 5; i++) {\n starPicker.innerHTML += buildStar('full');\n }\n\n const stars = Array.prototype.slice.call(starPicker.children);\n\n // If default value, we already highlight some stars\n const defaultValue = parseInt(starPicker.getAttribute('value'), 10);\n if (defaultValue && defaultValue <= 5 && defaultValue >= 1) {\n activateStars(stars, defaultValue - 1, starPicker);\n }\n\n // If we have a size specified, we apply it to the star rater\n const size = starPicker.getAttribute('data-font-size');\n if (size) {\n starPicker.style.fontSize = size;\n }\n\n // Place hover and click events listeners on stars\n stars.forEach((star, i) => {\n star.addEventListener('mouseenter', () => onStarMouseEnter(stars, i));\n star.addEventListener('mouseleave', () => onStarMouseLeave(stars));\n star.addEventListener('click', () => activateStars(stars, i, starPicker));\n });\n};\n\n/**\n * On mouse enter in star picker's star, we add classes to every stars\n * from the first to the one being hovered.\n *\n * @param star : Array<HTMLElement>\n * @param hoveredIndex : number\n */\nconst onStarMouseEnter = (stars: HTMLElement[], hoveredIndex: number) => {\n for (let i = 0; i <= hoveredIndex; i++) {\n stars[i].classList.add('dsg-hovered');\n }\n};\n\n/**\n * On mouse leave of star picker's star, we remove all the hovered classes of all stars\n *\n * @param star : Array<HTMLElement>\n */\nconst onStarMouseLeave = (stars: HTMLElement[]) => {\n stars.forEach(star => star.classList.remove('dsg-hovered'));\n};\n\n/**\n * On mouse click on star picker's star, we add classes to every stars\n * from the first to the one being clicked.\n *\n * @param star : Array<HTMLElement>\n * @param hoveredIndex : number\n * @param starPicker : HTMLElement\n */\nconst activateStars = (stars: HTMLElement[], clickedIndex, starPicker: HTMLElement) => {\n\n // We add a class to every previous siblings of the clicked STAR\n for (let i = 0; i < stars.length; i++) {\n stars[i].classList.toggle('dsg-active', i <= clickedIndex);\n }\n\n // We insert the new star picker value in the input\n starPicker.setAttribute('value', clickedIndex + 1);\n cleanDispatchEvent('star-picker-update');\n};\n"
},
{
"filePath":"/pwd/styleguide/source/js/scripts/star-rater.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"\n/**\n * Author: D B\n * DateTime: 19/01/08\n * LastUpdated: 19/01/10\n * Description: Construct and handle behaviour of star-raters (ratings)\n */\n\nimport {\n getArrayByClassName,\n} from '../dsg-util';\n\n/**\n * First build and listening to events to reset builds\n */\nexport const initStarRaters = () => {\n buildStarRaters();\n window.addEventListener('dsg-reset-star-raters', buildStarRaters);\n};\n\n/**\n * Search for root elements to build the star raters\n */\nconst buildStarRaters = () => {\n const starRaters = getArrayByClassName('dsg-star-rater');\n starRaters.forEach(buildStarRater);\n};\n\n/**\n * Build a star rater by reading HTML attributes\n *\n * @param starRater : HTMLElement\n */\nconst buildStarRater = (starRater: HTMLElement) => {\n\n // Empty star rater in case we are resetting it\n starRater.innerHTML = '';\n\n // Get a round score out of 10\n const score = Math.round(parseFloat(starRater.getAttribute('data-score')) * 2);\n\n // Iterating on score to determine how many stars and half star should be displayed\n for (let i = 0; i < score ; i += 2) {\n\n // Number of stars cannot exceed 5\n if (i >= 10) {\n console.error('Error : The average value of the star-rater is too damn high');\n break;\n }\n\n // We only create a half star if we have an odd value (on rating out of 10)\n const starType = score % 2 === 1 && i === score - 1 ? 'half' : 'full' ;\n starRater.innerHTML += buildStar(starType);\n }\n\n // If we have a size specified, we apply it to the star rater\n const size = starRater.getAttribute('data-font-size');\n if (size) {\n starRater.style.fontSize = size;\n }\n\n // If we have a counter, we add it next to the star rater\n const counter = parseInt(starRater.getAttribute('data-review-counter'), 10);\n if (counter) {\n starRater.innerHTML += '<span class=\"dsg-star-rater__counter\">' + counter + '</span>';\n }\n};\n\n/**\n * Return a full star or a half star icon\n */\nexport const buildStar = (type: string) => {\n return `<i class=\"dsg-star-rater__star dsg-icon dsg-icon-star-${type}\"></i>`;\n};\n"
},
{
"filePath":"/pwd/styleguide/source/js/scripts-init.tsx",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"\n/**\n * Author: D B\n * DateTime: 18/11/07\n * Description: Initiate all scripts not using framework libs like react\n */\n\nimport initCssVars from 'css-vars-ponyfill';\nimport { initTabsMenu } from './scripts/scroll-menu';\nimport { initStarPickers } from './scripts/star-picker';\nimport { initStarRaters } from './scripts/star-rater';\n\n/**\n * Initiate all scripts not using framework libs like react\n */\nexport const initScripts = () => {\n initTabsMenu();\n initStarRaters();\n initStarPickers();\n\n // Initiate CSS variables ponyfill\n initCssVars();\n};\n"
},
{
"filePath":"/pwd/styleguide/source/js/setupTests.js",
"messages":[
{
"ruleId":null,
"fatal":true,
"severity":2,
"message":"Parsing error: File '/pwd/styleguide/source/js/tsconfig.json' not found."
}
],
"errorCount":1,
"warningCount":0,
"fixableErrorCount":0,
"fixableWarningCount":0,
"source":"import Enzyme from 'enzyme';\nimport Adapter from 'enzyme-adapter-react-16';\nimport {createSerializer} from 'enzyme-to-json';\n\nEnzyme.configure({ adapter: new Adapter() });\nexpect.addSnapshotSerializer(createSerializer({mode: 'deep'}));"
}
]
This doesn't seem like an issue with this action, but your ESLint/TypeScript setup: File '/pwd/styleguide/source/js/tsconfig.json' not found
@samuelmeuli Thanks I will investigate this.
Thanks so much for your help :+1:
No worries, let me know if you manage to resolve this :)
The action should probably detect such errors though and log a more helpful error message.
FIrst Thanks for the action, it works great for stylelint but somehow i canoot get it to work with ESLINT.
The task always fails (also tried to run only this task for ESLINT)
This is the error i get in the console of github actions.
Thanks for looking into this
data:
{"message":"Invalid request.\n\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.\n\"end_line\", \"start_line\" weren't supplied.","documentation_url":"https://developer.github.com/v3/checks/runs/#create-a-check-run"}} Error: Error trying to create GitHub check for ESLint: Received status code 422 at Object.createCheck (/home/runner/work/_actions/samuelmeuli/lint-action/v1.2.0/src/github.js:113:9) at processTicksAndRejections (internal/process/task_queues.js:93:5) /home/runner/work/_actions/samuelmeuli/lint-action/v1.2.0/src/index.js:15 throw new Error(
Exiting because of unhandled promise rejection`); ^Error: Exiting because of unhandled promise rejection at process. (/home/runner/work/_actions/samuelmeuli/lint-action/v1.2.0/src/index.js:15:8)
at process.emit (events.js:210:5)
at processPromiseRejections (internal/process/promises.js:201:33)
at processTicksAndRejections (internal/process/task_queues.js:94:32)
`