@popsure/dirty-swan 0.66.24 → 0.66.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/cjs/index.d.ts +1 -1
  2. package/dist/cjs/index.js +38 -0
  3. package/dist/cjs/index.js.map +1 -1
  4. package/dist/cjs/lib/context.d.ts +13 -0
  5. package/dist/cjs/lib/context.test.d.ts +1 -0
  6. package/dist/cjs/lib/index.d.ts +2 -1
  7. package/dist/esm/components/modal/bottomModal/index.js +1 -0
  8. package/dist/esm/components/modal/bottomModal/index.js.map +1 -1
  9. package/dist/esm/components/modal/bottomOrRegularModal/index.js +1 -0
  10. package/dist/esm/components/modal/bottomOrRegularModal/index.js.map +1 -1
  11. package/dist/esm/components/modal/fullScreenModal/index.js +1 -0
  12. package/dist/esm/components/modal/fullScreenModal/index.js.map +1 -1
  13. package/dist/esm/components/modal/genericModal/index.js +2 -0
  14. package/dist/esm/components/modal/genericModal/index.js.map +1 -1
  15. package/dist/esm/components/modal/index.stories.js +1 -0
  16. package/dist/esm/components/modal/index.stories.js.map +1 -1
  17. package/dist/esm/components/modal/regularModal/index.js +1 -0
  18. package/dist/esm/components/modal/regularModal/index.js.map +1 -1
  19. package/dist/esm/components/table/Table.js +1 -0
  20. package/dist/esm/components/table/Table.js.map +1 -1
  21. package/dist/esm/components/table/Table.stories.js +1 -0
  22. package/dist/esm/components/table/Table.stories.js.map +1 -1
  23. package/dist/esm/components/table/Table.test.js +1 -0
  24. package/dist/esm/components/table/Table.test.js.map +1 -1
  25. package/dist/esm/context.js +39 -0
  26. package/dist/esm/context.js.map +1 -0
  27. package/dist/esm/context.stories.js +67 -0
  28. package/dist/esm/context.stories.js.map +1 -0
  29. package/dist/esm/context.test.js +58 -0
  30. package/dist/esm/context.test.js.map +1 -0
  31. package/dist/esm/index.d.ts +1 -1
  32. package/dist/esm/index.js +1 -0
  33. package/dist/esm/index.js.map +1 -1
  34. package/dist/esm/lib/context.d.ts +13 -0
  35. package/dist/esm/lib/context.test.d.ts +1 -0
  36. package/dist/esm/lib/index.d.ts +2 -1
  37. package/package.json +1 -1
  38. package/src/index.tsx +3 -0
  39. package/src/lib/components/modal/genericModal/index.tsx +2 -0
  40. package/src/lib/context.stories.tsx +136 -0
  41. package/src/lib/context.test.tsx +88 -0
  42. package/src/lib/context.tsx +60 -0
  43. package/src/lib/index.tsx +4 -0
@@ -1 +1 @@
1
- {"version":3,"file":"Table.test.js","sources":["../../../../../src/lib/components/table/Table.test.tsx"],"sourcesContent":["import { render, screen } from '../../util/testUtils';\nimport { Table } from './Table';\nimport { TableSectionData } from './types';\n\nconst tableData: TableSectionData[] = [\n {\n section: {\n title: 'Section 1',\n icon: <span>Icon 1</span>,\n },\n rows: [[{ text: 'Item 1', cellId: '#item1' }, { text: 'Item 2' }]],\n },\n {\n section: {\n title: 'Section 2',\n icon: 'https://placehold.co/30x30/orange/white',\n },\n rows: [\n [{ text: 'Item 3' }, { text: 'Item 4', modalContent: 'Additional item' }],\n ],\n },\n];\n\ndescribe('Table', () => {\n it('renders the table with sections and items', () => {\n render(<Table tableData={tableData} title=\"Test Table\" />);\n\n expect(screen.getByText('Section 1')).toBeInTheDocument();\n expect(screen.getByText('Section 2')).toBeInTheDocument();\n\n expect(screen.getAllByText('Item 1')[0]).toBeInTheDocument();\n expect(screen.getAllByText('Item 2')[0]).toBeInTheDocument();\n expect(screen.getByText('Item 3')).toBeInTheDocument();\n expect(screen.getByText('Item 4')).toBeInTheDocument();\n });\n\n it('renders the table without sections', () => {\n const dataWithoutSections = [{ rows: tableData[0].rows }];\n\n render(<Table tableData={dataWithoutSections} title=\"Test Table\" />);\n\n expect(screen.getAllByText('Item 1')[0]).toBeVisible();\n expect(screen.getAllByText('Item 2')[0]).toBeVisible();\n });\n\n it('hides the show/hide details button if hideDetails is false', () => {\n render(<Table tableData={tableData} hideDetails title=\"Test Table\" />);\n\n expect(screen.queryByTestId('show-hide-details')).toBeInTheDocument();\n });\n\n it('shows the show/hide details button if hideDetails is true', () => {\n render(<Table tableData={tableData} hideDetails title=\"Test Table\" />);\n\n expect(screen.getByTestId('show-hide-details')).toBeInTheDocument();\n });\n\n it('hides the sections if hideDetails is true', () => {\n render(<Table tableData={tableData} hideDetails title=\"Test Table\" />);\n\n expect(screen.queryByText('Section 2')).not.toBeInTheDocument();\n expect(screen.getByTestId('show-hide-details')).toBeInTheDocument();\n });\n\n it('hides the sections if hideDetails is true and shows if clicking on button', async () => {\n const { user } = render(\n <Table tableData={tableData} hideDetails title=\"Test Table\" />\n );\n\n expect(screen.getByText('Show details')).toBeVisible();\n\n await user.click(screen.getByTestId('show-hide-details'));\n\n expect(screen.getByText('Section 2')).toBeVisible();\n expect(screen.getByText('Hide details')).toBeVisible();\n });\n\n it('does not render the sticky header row when hideStickyHeader is true', () => {\n const { rerender } = render(\n <Table tableData={tableData} title=\"Test Table\" />\n );\n const countWithStickyHeader = screen.getAllByText('Item 1').length;\n\n rerender(\n <Table tableData={tableData} hideStickyHeader title=\"Test Table\" />\n );\n\n expect(screen.getAllByText('Item 1')).toHaveLength(\n countWithStickyHeader - 1\n );\n });\n\n it('shows a modal when clicking on info icon', async () => {\n const { user } = render(<Table tableData={tableData} title=\"Test Table\" />);\n\n await user.click(screen.getByTestId('ds-table-info-button'));\n\n expect(screen.getByText('Additional item')).toBeVisible();\n });\n\n it('renders the table with cell replacements', () => {\n render(\n <Table\n tableData={tableData}\n title=\"Test Table\"\n cellReplacements={{\n '#item1': {\n text: 'I was replaced',\n },\n }}\n />\n );\n\n expect(screen.getByText('Section 1')).toBeInTheDocument();\n expect(screen.getByText('Section 2')).toBeInTheDocument();\n\n expect(screen.getAllByText('I was replaced')[0]).toBeInTheDocument();\n expect(screen.getAllByText('Item 2')[0]).toBeInTheDocument();\n expect(screen.getAllByText('Item 3')[0]).toBeInTheDocument();\n expect(screen.getAllByText('Item 4')[0]).toBeInTheDocument();\n });\n\n it('renders icons with a custom image component', () => {\n const CustomImageComponent = ({ src }: { src: string }) => (\n <>\n <p>I am custom</p>\n <img src={src} alt=\"\" />\n </>\n );\n\n render(\n <Table\n tableData={tableData}\n title=\"Test Table\"\n imageComponent={CustomImageComponent}\n />\n );\n\n expect(screen.getByText('I am custom')).toBeInTheDocument();\n });\n});\n"],"names":["_jsx","render","_jsxs","_Fragment"],"mappings":"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"}
1
+ {"version":3,"file":"Table.test.js","sources":["../../../../../src/lib/components/table/Table.test.tsx"],"sourcesContent":["import { render, screen } from '../../util/testUtils';\nimport { Table } from './Table';\nimport { TableSectionData } from './types';\n\nconst tableData: TableSectionData[] = [\n {\n section: {\n title: 'Section 1',\n icon: <span>Icon 1</span>,\n },\n rows: [[{ text: 'Item 1', cellId: '#item1' }, { text: 'Item 2' }]],\n },\n {\n section: {\n title: 'Section 2',\n icon: 'https://placehold.co/30x30/orange/white',\n },\n rows: [\n [{ text: 'Item 3' }, { text: 'Item 4', modalContent: 'Additional item' }],\n ],\n },\n];\n\ndescribe('Table', () => {\n it('renders the table with sections and items', () => {\n render(<Table tableData={tableData} title=\"Test Table\" />);\n\n expect(screen.getByText('Section 1')).toBeInTheDocument();\n expect(screen.getByText('Section 2')).toBeInTheDocument();\n\n expect(screen.getAllByText('Item 1')[0]).toBeInTheDocument();\n expect(screen.getAllByText('Item 2')[0]).toBeInTheDocument();\n expect(screen.getByText('Item 3')).toBeInTheDocument();\n expect(screen.getByText('Item 4')).toBeInTheDocument();\n });\n\n it('renders the table without sections', () => {\n const dataWithoutSections = [{ rows: tableData[0].rows }];\n\n render(<Table tableData={dataWithoutSections} title=\"Test Table\" />);\n\n expect(screen.getAllByText('Item 1')[0]).toBeVisible();\n expect(screen.getAllByText('Item 2')[0]).toBeVisible();\n });\n\n it('hides the show/hide details button if hideDetails is false', () => {\n render(<Table tableData={tableData} hideDetails title=\"Test Table\" />);\n\n expect(screen.queryByTestId('show-hide-details')).toBeInTheDocument();\n });\n\n it('shows the show/hide details button if hideDetails is true', () => {\n render(<Table tableData={tableData} hideDetails title=\"Test Table\" />);\n\n expect(screen.getByTestId('show-hide-details')).toBeInTheDocument();\n });\n\n it('hides the sections if hideDetails is true', () => {\n render(<Table tableData={tableData} hideDetails title=\"Test Table\" />);\n\n expect(screen.queryByText('Section 2')).not.toBeInTheDocument();\n expect(screen.getByTestId('show-hide-details')).toBeInTheDocument();\n });\n\n it('hides the sections if hideDetails is true and shows if clicking on button', async () => {\n const { user } = render(\n <Table tableData={tableData} hideDetails title=\"Test Table\" />\n );\n\n expect(screen.getByText('Show details')).toBeVisible();\n\n await user.click(screen.getByTestId('show-hide-details'));\n\n expect(screen.getByText('Section 2')).toBeVisible();\n expect(screen.getByText('Hide details')).toBeVisible();\n });\n\n it('does not render the sticky header row when hideStickyHeader is true', () => {\n const { rerender } = render(\n <Table tableData={tableData} title=\"Test Table\" />\n );\n const countWithStickyHeader = screen.getAllByText('Item 1').length;\n\n rerender(\n <Table tableData={tableData} hideStickyHeader title=\"Test Table\" />\n );\n\n expect(screen.getAllByText('Item 1')).toHaveLength(\n countWithStickyHeader - 1\n );\n });\n\n it('shows a modal when clicking on info icon', async () => {\n const { user } = render(<Table tableData={tableData} title=\"Test Table\" />);\n\n await user.click(screen.getByTestId('ds-table-info-button'));\n\n expect(screen.getByText('Additional item')).toBeVisible();\n });\n\n it('renders the table with cell replacements', () => {\n render(\n <Table\n tableData={tableData}\n title=\"Test Table\"\n cellReplacements={{\n '#item1': {\n text: 'I was replaced',\n },\n }}\n />\n );\n\n expect(screen.getByText('Section 1')).toBeInTheDocument();\n expect(screen.getByText('Section 2')).toBeInTheDocument();\n\n expect(screen.getAllByText('I was replaced')[0]).toBeInTheDocument();\n expect(screen.getAllByText('Item 2')[0]).toBeInTheDocument();\n expect(screen.getAllByText('Item 3')[0]).toBeInTheDocument();\n expect(screen.getAllByText('Item 4')[0]).toBeInTheDocument();\n });\n\n it('renders icons with a custom image component', () => {\n const CustomImageComponent = ({ src }: { src: string }) => (\n <>\n <p>I am custom</p>\n <img src={src} alt=\"\" />\n </>\n );\n\n render(\n <Table\n tableData={tableData}\n title=\"Test Table\"\n imageComponent={CustomImageComponent}\n />\n );\n\n expect(screen.getByText('I am custom')).toBeInTheDocument();\n });\n});\n"],"names":["_jsx","render","_jsxs","_Fragment"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA,IAAM,SAAS,GAAuB;AACpC,IAAA;AACE,QAAA,OAAO,EAAE;AACP,YAAA,KAAK,EAAE,WAAW;AAClB,YAAA,IAAI,EAAEA,GAAA,CAAA,MAAA,EAAA,EAAA,QAAA,EAAA,QAAA,EAAA,CAAmB;AAC1B,SAAA;AACD,QAAA,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;AACnE,KAAA;AACD,IAAA;AACE,QAAA,OAAO,EAAE;AACP,YAAA,KAAK,EAAE,WAAW;AAClB,YAAA,IAAI,EAAE,yCAAyC;AAChD,SAAA;AACD,QAAA,IAAI,EAAE;AACJ,YAAA,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,iBAAiB,EAAE,CAAC;AAC1E,SAAA;AACF,KAAA;CACF;AAED,QAAQ,CAAC,OAAO,EAAE,YAAA;IAChB,EAAE,CAAC,2CAA2C,EAAE,YAAA;AAC9C,QAAAC,YAAM,CAACD,GAAA,CAAC,KAAK,EAAA,EAAC,SAAS,EAAE,SAAS,EAAE,KAAK,EAAC,YAAY,EAAA,CAAG,CAAC;QAE1D,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,iBAAiB,EAAE;QACzD,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,iBAAiB,EAAE;AAEzD,QAAA,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,iBAAiB,EAAE;AAC5D,QAAA,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,iBAAiB,EAAE;QAC5D,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,iBAAiB,EAAE;QACtD,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,iBAAiB,EAAE;AACxD,IAAA,CAAC,CAAC;IAEF,EAAE,CAAC,oCAAoC,EAAE,YAAA;AACvC,QAAA,IAAM,mBAAmB,GAAG,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AAEzD,QAAAC,YAAM,CAACD,GAAA,CAAC,KAAK,EAAA,EAAC,SAAS,EAAE,mBAAmB,EAAE,KAAK,EAAC,YAAY,EAAA,CAAG,CAAC;AAEpE,QAAA,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE;AACtD,QAAA,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE;AACxD,IAAA,CAAC,CAAC;IAEF,EAAE,CAAC,4DAA4D,EAAE,YAAA;AAC/D,QAAAC,YAAM,CAACD,GAAA,CAAC,KAAK,EAAA,EAAC,SAAS,EAAE,SAAS,EAAE,WAAW,QAAC,KAAK,EAAC,YAAY,EAAA,CAAG,CAAC;QAEtE,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,mBAAmB,CAAC,CAAC,CAAC,iBAAiB,EAAE;AACvE,IAAA,CAAC,CAAC;IAEF,EAAE,CAAC,2DAA2D,EAAE,YAAA;AAC9D,QAAAC,YAAM,CAACD,GAAA,CAAC,KAAK,EAAA,EAAC,SAAS,EAAE,SAAS,EAAE,WAAW,QAAC,KAAK,EAAC,YAAY,EAAA,CAAG,CAAC;QAEtE,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,mBAAmB,CAAC,CAAC,CAAC,iBAAiB,EAAE;AACrE,IAAA,CAAC,CAAC;IAEF,EAAE,CAAC,2CAA2C,EAAE,YAAA;AAC9C,QAAAC,YAAM,CAACD,GAAA,CAAC,KAAK,EAAA,EAAC,SAAS,EAAE,SAAS,EAAE,WAAW,QAAC,KAAK,EAAC,YAAY,EAAA,CAAG,CAAC;AAEtE,QAAA,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,iBAAiB,EAAE;QAC/D,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,mBAAmB,CAAC,CAAC,CAAC,iBAAiB,EAAE;AACrE,IAAA,CAAC,CAAC;IAEF,EAAE,CAAC,2EAA2E,EAAE,YAAA,EAAA,OAAA,SAAA,CAAA,MAAA,EAAA,MAAA,EAAA,MAAA,EAAA,YAAA;;;;;AACtE,oBAAA,IAAI,GAAKC,YAAM,CACrBD,IAAC,KAAK,EAAA,EAAC,SAAS,EAAE,SAAS,EAAE,WAAW,QAAC,KAAK,EAAC,YAAY,EAAA,CAAG,CAC/D,KAFW;oBAIZ,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,CAAC,WAAW,EAAE;oBAEtD,OAAA,CAAA,CAAA,YAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,mBAAmB,CAAC,CAAC,CAAA;;AAAzD,oBAAA,EAAA,CAAA,IAAA,EAAyD;oBAEzD,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW,EAAE;oBACnD,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,CAAC,WAAW,EAAE;;;;AACvD,IAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAC;IAEF,EAAE,CAAC,qEAAqE,EAAE,YAAA;AAChE,QAAA,IAAA,QAAQ,GAAKC,YAAM,CACzBD,GAAA,CAAC,KAAK,EAAA,EAAC,SAAS,EAAE,SAAS,EAAE,KAAK,EAAC,YAAY,EAAA,CAAG,CACnD,SAFe;QAGhB,IAAM,qBAAqB,GAAG,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,MAAM;AAElE,QAAA,QAAQ,CACNA,GAAA,CAAC,KAAK,EAAA,EAAC,SAAS,EAAE,SAAS,EAAE,gBAAgB,QAAC,KAAK,EAAC,YAAY,EAAA,CAAG,CACpE;AAED,QAAA,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,CAChD,qBAAqB,GAAG,CAAC,CAC1B;AACH,IAAA,CAAC,CAAC;IAEF,EAAE,CAAC,0CAA0C,EAAE,YAAA,EAAA,OAAA,SAAA,CAAA,MAAA,EAAA,MAAA,EAAA,MAAA,EAAA,YAAA;;;;;AACrC,oBAAA,IAAI,GAAKC,YAAM,CAACD,GAAA,CAAC,KAAK,IAAC,SAAS,EAAE,SAAS,EAAE,KAAK,EAAC,YAAY,EAAA,CAAG,CAAC,KAA/D;oBAEZ,OAAA,CAAA,CAAA,YAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,sBAAsB,CAAC,CAAC,CAAA;;AAA5D,oBAAA,EAAA,CAAA,IAAA,EAA4D;oBAE5D,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC,CAAC,WAAW,EAAE;;;;AAC1D,IAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAC;IAEF,EAAE,CAAC,0CAA0C,EAAE,YAAA;AAC7C,QAAAC,YAAM,CACJD,GAAA,CAAC,KAAK,EAAA,EACJ,SAAS,EAAE,SAAS,EACpB,KAAK,EAAC,YAAY,EAClB,gBAAgB,EAAE;AAChB,gBAAA,QAAQ,EAAE;AACR,oBAAA,IAAI,EAAE,gBAAgB;AACvB,iBAAA;AACF,aAAA,EAAA,CACD,CACH;QAED,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,iBAAiB,EAAE;QACzD,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,iBAAiB,EAAE;AAEzD,QAAA,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,iBAAiB,EAAE;AACpE,QAAA,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,iBAAiB,EAAE;AAC5D,QAAA,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,iBAAiB,EAAE;AAC5D,QAAA,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,iBAAiB,EAAE;AAC9D,IAAA,CAAC,CAAC;IAEF,EAAE,CAAC,6CAA6C,EAAE,YAAA;QAChD,IAAM,oBAAoB,GAAG,UAAC,EAAwB,EAAA;AAAtB,YAAA,IAAA,GAAG,GAAA,EAAA,CAAA,GAAA;AAAwB,YAAA,QACzDE,IAAA,CAAAC,QAAA,EAAA,EAAA,QAAA,EAAA,CACEH,GAAA,CAAA,GAAA,EAAA,EAAA,QAAA,EAAA,aAAA,EAAA,CAAkB,EAClBA,aAAK,GAAG,EAAE,GAAG,EAAE,GAAG,EAAC,EAAE,EAAA,CAAG,IACvB;AAJsD,QAAA,CAK1D;AAED,QAAAC,YAAM,CACJD,GAAA,CAAC,KAAK,EAAA,EACJ,SAAS,EAAE,SAAS,EACpB,KAAK,EAAC,YAAY,EAClB,cAAc,EAAE,oBAAoB,EAAA,CACpC,CACH;QAED,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,iBAAiB,EAAE;AAC7D,IAAA,CAAC,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,39 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { createContext, useRef, useCallback, useMemo, useContext, useEffect } from 'react';
3
+
4
+ var DirtySwanContext = createContext(null);
5
+ var DirtySwanProvider = function (_a) {
6
+ var children = _a.children, onModalChange = _a.onModalChange;
7
+ var countRef = useRef(0);
8
+ var onModalChangeRef = useRef(onModalChange);
9
+ onModalChangeRef.current = onModalChange;
10
+ var onModalOpen = useCallback(function () {
11
+ var _a;
12
+ countRef.current += 1;
13
+ if (countRef.current === 1) {
14
+ (_a = onModalChangeRef.current) === null || _a === void 0 ? void 0 : _a.call(onModalChangeRef, true);
15
+ }
16
+ }, []);
17
+ var onModalClose = useCallback(function () {
18
+ var _a;
19
+ countRef.current -= 1;
20
+ if (countRef.current === 0) {
21
+ (_a = onModalChangeRef.current) === null || _a === void 0 ? void 0 : _a.call(onModalChangeRef, false);
22
+ }
23
+ }, []);
24
+ var value = useMemo(function () { return ({ onModalOpen: onModalOpen, onModalClose: onModalClose }); }, [onModalOpen, onModalClose]);
25
+ return (jsx(DirtySwanContext.Provider, { value: value, children: children }));
26
+ };
27
+ var useDirtySwan = function () { return useContext(DirtySwanContext); };
28
+ var useModalTrack = function (isVisible) {
29
+ var dirtySwan = useDirtySwan();
30
+ useEffect(function () {
31
+ if (!isVisible)
32
+ return;
33
+ dirtySwan === null || dirtySwan === void 0 ? void 0 : dirtySwan.onModalOpen();
34
+ return function () { return dirtySwan === null || dirtySwan === void 0 ? void 0 : dirtySwan.onModalClose(); };
35
+ }, [isVisible, dirtySwan]);
36
+ };
37
+
38
+ export { DirtySwanProvider, useDirtySwan, useModalTrack };
39
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sources":["../../../src/lib/context.tsx"],"sourcesContent":["import { createContext, ReactNode, useCallback, useContext, useEffect, useMemo, useRef } from 'react';\n\ninterface DirtySwanContextValue {\n onModalOpen: () => void;\n onModalClose: () => void;\n}\n\nconst DirtySwanContext = createContext<DirtySwanContextValue | null>(null);\n\ninterface DirtySwanProviderProps {\n children: ReactNode;\n onModalChange?: (isModalOpen: boolean) => void;\n}\n\nexport const DirtySwanProvider = ({\n children,\n onModalChange,\n}: DirtySwanProviderProps) => {\n const countRef = useRef(0);\n const onModalChangeRef = useRef(onModalChange);\n onModalChangeRef.current = onModalChange;\n\n const onModalOpen = useCallback(() => {\n countRef.current += 1;\n if (countRef.current === 1) {\n onModalChangeRef.current?.(true);\n }\n }, []);\n\n const onModalClose = useCallback(() => {\n countRef.current -= 1;\n if (countRef.current === 0) {\n onModalChangeRef.current?.(false);\n }\n }, []);\n\n const value = useMemo(\n () => ({ onModalOpen, onModalClose }),\n [onModalOpen, onModalClose]\n );\n\n return (\n <DirtySwanContext.Provider value={value}>\n {children}\n </DirtySwanContext.Provider>\n );\n};\n\nexport const useDirtySwan = () => useContext(DirtySwanContext);\n\nexport const useModalTrack = (isVisible: boolean) => {\n const dirtySwan = useDirtySwan();\n\n useEffect(() => {\n if (!isVisible) return;\n\n dirtySwan?.onModalOpen();\n return () => dirtySwan?.onModalClose();\n }, [isVisible, dirtySwan]);\n};\n"],"names":["_jsx"],"mappings":";;;AAOA,IAAM,gBAAgB,GAAG,aAAa,CAA+B,IAAI,CAAC;AAOnE,IAAM,iBAAiB,GAAG,UAAC,EAGT,EAAA;QAFvB,QAAQ,GAAA,EAAA,CAAA,QAAA,EACR,aAAa,GAAA,EAAA,CAAA,aAAA;AAEb,IAAA,IAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC;AAC1B,IAAA,IAAM,gBAAgB,GAAG,MAAM,CAAC,aAAa,CAAC;AAC9C,IAAA,gBAAgB,CAAC,OAAO,GAAG,aAAa;IAExC,IAAM,WAAW,GAAG,WAAW,CAAC,YAAA;;AAC9B,QAAA,QAAQ,CAAC,OAAO,IAAI,CAAC;AACrB,QAAA,IAAI,QAAQ,CAAC,OAAO,KAAK,CAAC,EAAE;AAC1B,YAAA,CAAA,EAAA,GAAA,gBAAgB,CAAC,OAAO,MAAA,IAAA,IAAA,EAAA,KAAA,MAAA,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,CAAA,gBAAA,EAAG,IAAI,CAAC;QAClC;IACF,CAAC,EAAE,EAAE,CAAC;IAEN,IAAM,YAAY,GAAG,WAAW,CAAC,YAAA;;AAC/B,QAAA,QAAQ,CAAC,OAAO,IAAI,CAAC;AACrB,QAAA,IAAI,QAAQ,CAAC,OAAO,KAAK,CAAC,EAAE;AAC1B,YAAA,CAAA,EAAA,GAAA,gBAAgB,CAAC,OAAO,MAAA,IAAA,IAAA,EAAA,KAAA,MAAA,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,CAAA,gBAAA,EAAG,KAAK,CAAC;QACnC;IACF,CAAC,EAAE,EAAE,CAAC;IAEN,IAAM,KAAK,GAAG,OAAO,CACnB,YAAA,EAAM,QAAC,EAAE,WAAW,EAAA,WAAA,EAAE,YAAY,cAAA,EAAE,EAAC,CAA/B,CAA+B,EACrC,CAAC,WAAW,EAAE,YAAY,CAAC,CAC5B;AAED,IAAA,QACEA,GAAA,CAAC,gBAAgB,CAAC,QAAQ,EAAA,EAAC,KAAK,EAAE,KAAK,EAAA,QAAA,EACpC,QAAQ,EAAA,CACiB;AAEhC;AAEO,IAAM,YAAY,GAAG,YAAA,EAAM,OAAA,UAAU,CAAC,gBAAgB,CAAC,CAAA,CAA5B;AAE3B,IAAM,aAAa,GAAG,UAAC,SAAkB,EAAA;AAC9C,IAAA,IAAM,SAAS,GAAG,YAAY,EAAE;AAEhC,IAAA,SAAS,CAAC,YAAA;AACR,QAAA,IAAI,CAAC,SAAS;YAAE;AAEhB,QAAA,SAAS,aAAT,SAAS,KAAA,MAAA,GAAA,MAAA,GAAT,SAAS,CAAE,WAAW,EAAE;AACxB,QAAA,OAAO,YAAA,EAAM,OAAA,SAAS,KAAA,IAAA,IAAT,SAAS,KAAA,MAAA,GAAA,MAAA,GAAT,SAAS,CAAE,YAAY,EAAE,CAAA,CAAzB,CAAyB;AACxC,IAAA,CAAC,EAAE,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;AAC5B;;;;"}
@@ -0,0 +1,67 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { useState } from 'react';
3
+ import { DirtySwanProvider } from './context.js';
4
+ import './index-DLQEsiym.js';
5
+ import { Button } from './components/button/index.js';
6
+ import { BottomOrRegularModal } from './components/modal/bottomOrRegularModal/index.js';
7
+ import './_commonjsHelpers-BFTU3MAI.js';
8
+ import './tslib.es6-CVlKGIvp.js';
9
+ import './components/modal/bottomModal/index.js';
10
+ import './style-inject.es-tgCJW-Cu.js';
11
+ import './components/modal/genericModal/index.js';
12
+ import './components/icon/icons/X.js';
13
+ import './components/icon/IconWrapper/IconWrapper.js';
14
+ import './index-DuaJiEiT.js';
15
+ import './index-hWqCLhoS.js';
16
+ import './components/modal/regularModal/index.js';
17
+
18
+ var story = {
19
+ title: 'JSX/DirtySwanProvider',
20
+ component: DirtySwanProvider,
21
+ argTypes: {
22
+ onModalChange: {
23
+ description: 'Callback fired when the modal open state changes. Receives `true` when the first modal opens and `false` when the last modal closes.',
24
+ action: true,
25
+ table: {
26
+ category: 'Callbacks',
27
+ },
28
+ },
29
+ },
30
+ parameters: {
31
+ docs: {
32
+ description: {
33
+ component: 'An optional context provider that notifies consumers when any modal is open or closed. It tracks nested modals using a counter — `onModalChange(true)` fires when the first modal opens, and `onModalChange(false)` only fires when the last one closes.',
34
+ },
35
+ },
36
+ },
37
+ };
38
+ var SingleModal = {
39
+ render: function (_a) {
40
+ var onModalChange = _a.onModalChange;
41
+ var _b = useState(false), isOpen = _b[0], setIsOpen = _b[1];
42
+ var _c = useState(false), isModalOpen = _c[0], setIsModalOpen = _c[1];
43
+ var handleModalChange = function (open) {
44
+ setIsModalOpen(open);
45
+ onModalChange(open);
46
+ };
47
+ return (jsxs(DirtySwanProvider, { onModalChange: handleModalChange, children: [jsxs("div", { className: "d-flex fd-column gap16", children: [jsxs("div", { children: ["Modal state: ", jsx("strong", { children: isModalOpen ? 'open' : 'closed' })] }), jsx(Button, { className: "wmn2", onClick: function () { return setIsOpen(true); }, children: "Open modal" })] }), jsx(BottomOrRegularModal, { title: "Single modal", isOpen: isOpen, onClose: function () { return setIsOpen(false); }, children: jsxs("div", { style: { padding: '0 24px 24px 24px' }, children: ["Opening this modal triggered onModalChange(true). Closing it will trigger onModalChange(false).", jsx(Button, { className: "mt24 wmn3", onClick: function () { return setIsOpen(false); }, children: "Close" })] }) })] }));
48
+ },
49
+ name: 'Single modal',
50
+ };
51
+ var NestedModals = {
52
+ render: function (_a) {
53
+ var onModalChange = _a.onModalChange;
54
+ var _b = useState(false), isFirstOpen = _b[0], setIsFirstOpen = _b[1];
55
+ var _c = useState(false), isSecondOpen = _c[0], setIsSecondOpen = _c[1];
56
+ var _d = useState(false), isModalOpen = _d[0], setIsModalOpen = _d[1];
57
+ var handleModalChange = function (open) {
58
+ setIsModalOpen(open);
59
+ onModalChange(open);
60
+ };
61
+ return (jsxs(DirtySwanProvider, { onModalChange: handleModalChange, children: [jsxs("div", { className: "d-flex fd-column gap16", children: [jsxs("div", { children: ["Modal state: ", jsx("strong", { children: isModalOpen ? 'open' : 'closed' })] }), jsx(Button, { className: "wmn2", onClick: function () { return setIsFirstOpen(true); }, children: "Open first modal" })] }), jsx(BottomOrRegularModal, { title: "First modal", isOpen: isFirstOpen, onClose: function () { return setIsFirstOpen(false); }, children: jsxs("div", { style: { padding: '0 24px 24px 24px' }, children: ["This is the first modal. Open a second one to see that onModalChange(false) only fires when both are closed.", jsxs("div", { className: "d-flex fd-row gap8 mt24", children: [jsx(Button, { className: "wmn3", onClick: function () { return setIsSecondOpen(true); }, children: "Open second modal" }), jsx(Button, { variant: "textBlack", className: "wmn3", onClick: function () { return setIsFirstOpen(false); }, children: "Close" })] })] }) }), jsx(BottomOrRegularModal, { title: "Second modal", isOpen: isSecondOpen, onClose: function () { return setIsSecondOpen(false); }, children: jsxs("div", { style: { padding: '0 24px 24px 24px' }, children: ["This is the second (nested) modal. Closing this one will not trigger onModalChange(false) because the first modal is still open.", jsx(Button, { className: "mt24 wmn3", onClick: function () { return setIsSecondOpen(false); }, children: "Close" })] }) })] }));
62
+ },
63
+ name: 'Nested modals',
64
+ };
65
+
66
+ export { NestedModals, SingleModal, story as default };
67
+ //# sourceMappingURL=context.stories.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.stories.js","sources":["../../../src/lib/context.stories.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { DirtySwanProvider } from './context';\nimport { BottomOrRegularModal } from './components/modal';\nimport { Button } from './components/button';\n\nconst story = {\n title: 'JSX/DirtySwanProvider',\n component: DirtySwanProvider,\n argTypes: {\n onModalChange: {\n description:\n 'Callback fired when the modal open state changes. Receives `true` when the first modal opens and `false` when the last modal closes.',\n action: true,\n table: {\n category: 'Callbacks',\n },\n },\n },\n parameters: {\n docs: {\n description: {\n component:\n 'An optional context provider that notifies consumers when any modal is open or closed. It tracks nested modals using a counter — `onModalChange(true)` fires when the first modal opens, and `onModalChange(false)` only fires when the last one closes.',\n },\n },\n },\n};\n\nexport const SingleModal = {\n render: ({ onModalChange }: { onModalChange: (isOpen: boolean) => void }) => {\n const [isOpen, setIsOpen] = useState(false);\n const [isModalOpen, setIsModalOpen] = useState(false);\n\n const handleModalChange = (open: boolean) => {\n setIsModalOpen(open);\n onModalChange(open);\n };\n\n return (\n <DirtySwanProvider onModalChange={handleModalChange}>\n <div className=\"d-flex fd-column gap16\">\n <div>\n Modal state: <strong>{isModalOpen ? 'open' : 'closed'}</strong>\n </div>\n <Button className=\"wmn2\" onClick={() => setIsOpen(true)}>\n Open modal\n </Button>\n </div>\n\n <BottomOrRegularModal\n title=\"Single modal\"\n isOpen={isOpen}\n onClose={() => setIsOpen(false)}\n >\n <div style={{ padding: '0 24px 24px 24px' }}>\n Opening this modal triggered onModalChange(true). Closing it will\n trigger onModalChange(false).\n <Button className=\"mt24 wmn3\" onClick={() => setIsOpen(false)}>\n Close\n </Button>\n </div>\n </BottomOrRegularModal>\n </DirtySwanProvider>\n );\n },\n name: 'Single modal',\n};\n\nexport const NestedModals = {\n render: ({ onModalChange }: { onModalChange: (isOpen: boolean) => void }) => {\n const [isFirstOpen, setIsFirstOpen] = useState(false);\n const [isSecondOpen, setIsSecondOpen] = useState(false);\n const [isModalOpen, setIsModalOpen] = useState(false);\n\n const handleModalChange = (open: boolean) => {\n setIsModalOpen(open);\n onModalChange(open);\n };\n\n return (\n <DirtySwanProvider onModalChange={handleModalChange}>\n <div className=\"d-flex fd-column gap16\">\n <div>\n Modal state: <strong>{isModalOpen ? 'open' : 'closed'}</strong>\n </div>\n <Button className=\"wmn2\" onClick={() => setIsFirstOpen(true)}>\n Open first modal\n </Button>\n </div>\n\n <BottomOrRegularModal\n title=\"First modal\"\n isOpen={isFirstOpen}\n onClose={() => setIsFirstOpen(false)}\n >\n <div style={{ padding: '0 24px 24px 24px' }}>\n This is the first modal. Open a second one to see that\n onModalChange(false) only fires when both are closed.\n <div className=\"d-flex fd-row gap8 mt24\">\n <Button className=\"wmn3\" onClick={() => setIsSecondOpen(true)}>\n Open second modal\n </Button>\n <Button\n variant=\"textBlack\"\n className=\"wmn3\"\n onClick={() => setIsFirstOpen(false)}\n >\n Close\n </Button>\n </div>\n </div>\n </BottomOrRegularModal>\n\n <BottomOrRegularModal\n title=\"Second modal\"\n isOpen={isSecondOpen}\n onClose={() => setIsSecondOpen(false)}\n >\n <div style={{ padding: '0 24px 24px 24px' }}>\n This is the second (nested) modal. Closing this one will not trigger\n onModalChange(false) because the first modal is still open.\n <Button\n className=\"mt24 wmn3\"\n onClick={() => setIsSecondOpen(false)}\n >\n Close\n </Button>\n </div>\n </BottomOrRegularModal>\n </DirtySwanProvider>\n );\n },\n name: 'Nested modals',\n};\n\nexport default story;\n"],"names":["_jsxs","_jsx"],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { c as customRender, f as fireEvent, s as screen } from './customRender-DzdqZxUd.js';
3
+ import { DirtySwanProvider, useDirtySwan } from './context.js';
4
+ import './tslib.es6-CVlKGIvp.js';
5
+ import './_commonjsHelpers-BFTU3MAI.js';
6
+ import 'react';
7
+ import 'react-dom';
8
+ import 'util';
9
+ import 'react-dom/test-utils';
10
+
11
+ var TestConsumer = function () {
12
+ var ctx = useDirtySwan();
13
+ return (jsxs("div", { children: [jsx("button", { onClick: function () { return ctx === null || ctx === void 0 ? void 0 : ctx.onModalOpen(); }, children: "open" }), jsx("button", { onClick: function () { return ctx === null || ctx === void 0 ? void 0 : ctx.onModalClose(); }, children: "close" })] }));
14
+ };
15
+ describe('DirtySwanProvider', function () {
16
+ it('calls onModalChange(true) when first modal opens', function () {
17
+ var onModalChange = jest.fn();
18
+ customRender(jsx(DirtySwanProvider, { onModalChange: onModalChange, children: jsx(TestConsumer, {}) }));
19
+ fireEvent.click(screen.getByText('open'));
20
+ expect(onModalChange).toHaveBeenCalledWith(true);
21
+ });
22
+ it('calls onModalChange(false) when last modal closes', function () {
23
+ var onModalChange = jest.fn();
24
+ customRender(jsx(DirtySwanProvider, { onModalChange: onModalChange, children: jsx(TestConsumer, {}) }));
25
+ fireEvent.click(screen.getByText('open'));
26
+ fireEvent.click(screen.getByText('close'));
27
+ expect(onModalChange).toHaveBeenLastCalledWith(false);
28
+ });
29
+ it('does not call onModalChange(false) when nested modals are still open', function () {
30
+ var onModalChange = jest.fn();
31
+ customRender(jsx(DirtySwanProvider, { onModalChange: onModalChange, children: jsx(TestConsumer, {}) }));
32
+ fireEvent.click(screen.getByText('open'));
33
+ fireEvent.click(screen.getByText('open'));
34
+ fireEvent.click(screen.getByText('close'));
35
+ expect(onModalChange).toHaveBeenCalledTimes(1);
36
+ expect(onModalChange).toHaveBeenCalledWith(true);
37
+ });
38
+ it('calls onModalChange(false) when all nested modals close', function () {
39
+ var onModalChange = jest.fn();
40
+ customRender(jsx(DirtySwanProvider, { onModalChange: onModalChange, children: jsx(TestConsumer, {}) }));
41
+ fireEvent.click(screen.getByText('open'));
42
+ fireEvent.click(screen.getByText('open'));
43
+ fireEvent.click(screen.getByText('close'));
44
+ fireEvent.click(screen.getByText('close'));
45
+ expect(onModalChange).toHaveBeenCalledTimes(2);
46
+ expect(onModalChange).toHaveBeenLastCalledWith(false);
47
+ });
48
+ it('returns null when used outside provider', function () {
49
+ var contextValue = undefined;
50
+ var Spy = function () {
51
+ contextValue = useDirtySwan();
52
+ return null;
53
+ };
54
+ customRender(jsx(Spy, {}));
55
+ expect(contextValue).toBeNull();
56
+ });
57
+ });
58
+ //# sourceMappingURL=context.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.test.js","sources":["../../../src/lib/context.test.tsx"],"sourcesContent":["import { render, screen, fireEvent } from './util/testUtils';\nimport { DirtySwanProvider, useDirtySwan } from './context';\n\nconst TestConsumer = () => {\n const ctx = useDirtySwan();\n\n return (\n <div>\n <button onClick={() => ctx?.onModalOpen()}>open</button>\n <button onClick={() => ctx?.onModalClose()}>close</button>\n </div>\n );\n};\n\ndescribe('DirtySwanProvider', () => {\n it('calls onModalChange(true) when first modal opens', () => {\n const onModalChange = jest.fn();\n render(\n <DirtySwanProvider onModalChange={onModalChange}>\n <TestConsumer />\n </DirtySwanProvider>\n );\n\n fireEvent.click(screen.getByText('open'));\n\n expect(onModalChange).toHaveBeenCalledWith(true);\n });\n\n it('calls onModalChange(false) when last modal closes', () => {\n const onModalChange = jest.fn();\n render(\n <DirtySwanProvider onModalChange={onModalChange}>\n <TestConsumer />\n </DirtySwanProvider>\n );\n\n fireEvent.click(screen.getByText('open'));\n fireEvent.click(screen.getByText('close'));\n\n expect(onModalChange).toHaveBeenLastCalledWith(false);\n });\n\n it('does not call onModalChange(false) when nested modals are still open', () => {\n const onModalChange = jest.fn();\n render(\n <DirtySwanProvider onModalChange={onModalChange}>\n <TestConsumer />\n </DirtySwanProvider>\n );\n\n fireEvent.click(screen.getByText('open'));\n fireEvent.click(screen.getByText('open'));\n fireEvent.click(screen.getByText('close'));\n\n expect(onModalChange).toHaveBeenCalledTimes(1);\n expect(onModalChange).toHaveBeenCalledWith(true);\n });\n\n it('calls onModalChange(false) when all nested modals close', () => {\n const onModalChange = jest.fn();\n render(\n <DirtySwanProvider onModalChange={onModalChange}>\n <TestConsumer />\n </DirtySwanProvider>\n );\n\n fireEvent.click(screen.getByText('open'));\n fireEvent.click(screen.getByText('open'));\n fireEvent.click(screen.getByText('close'));\n fireEvent.click(screen.getByText('close'));\n\n expect(onModalChange).toHaveBeenCalledTimes(2);\n expect(onModalChange).toHaveBeenLastCalledWith(false);\n });\n\n it('returns null when used outside provider', () => {\n let contextValue: ReturnType<typeof useDirtySwan> = undefined as any;\n\n const Spy = () => {\n contextValue = useDirtySwan();\n return null;\n };\n\n render(<Spy />);\n\n expect(contextValue).toBeNull();\n });\n});\n"],"names":["_jsxs","_jsx","render"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- export { Accordion, DateSelector, SignaturePad, AutocompleteAddress, Input, MultiDropzone, IbanInput, CurrencyInput, BottomModal, RegularModal, BottomOrRegularModal, FullScreenModal, InfoCard, CardButton, Card, Button, AutoSuggestMultiSelect, Chip, AutoSuggestInput, ComparisonTable, TableRating, TableTrueFalse, TableRowHeader, TableButton, TableInfoButton, SegmentedControl, DownloadButton, Checkbox, Radio, Link, InformationBox, Badge, images, illustrations, Spinner, Table, Toggle, Toaster, toast, useEscapeKey, useFocusWithin, useMediaQuery, useOnClickOutside, LogoColor, LogoWhite, LogoBlack, LogoOrangeBox, LogoInvertedColor, LogoInverted, LogoPositiveColor, LogoPositiveBlack, SearchableDropdown, } from './lib';
1
+ export { Accordion, DateSelector, SignaturePad, AutocompleteAddress, Input, MultiDropzone, IbanInput, CurrencyInput, BottomModal, RegularModal, BottomOrRegularModal, FullScreenModal, InfoCard, CardButton, Card, Button, AutoSuggestMultiSelect, Chip, AutoSuggestInput, ComparisonTable, TableRating, TableTrueFalse, TableRowHeader, TableButton, TableInfoButton, SegmentedControl, DownloadButton, Checkbox, Radio, Link, InformationBox, Badge, images, illustrations, Spinner, Table, Toggle, Toaster, toast, useEscapeKey, useFocusWithin, useMediaQuery, useOnClickOutside, LogoColor, LogoWhite, LogoBlack, LogoOrangeBox, LogoInvertedColor, LogoInverted, LogoPositiveColor, LogoPositiveBlack, SearchableDropdown, DirtySwanProvider, useDirtySwan, useModalTrack, } from './lib';
2
2
  export * from './lib/components/icon';
3
3
  export type { AccordionProps, BadgeProps, DownloadStatus, InformationBoxProps, InputProps, IllustrationKeys, FileType, MultiDropzoneProps, TableHeader, UploadedFile, UploadStatus, CardProps, IconWrapperProps, TableData, TableProps, SearchableDropdownProps, SearchableDropdownOption, } from './lib';
package/dist/esm/index.js CHANGED
@@ -43,6 +43,7 @@ export { LogoInvertedColor } from './components/logo/LogoInvertedColor.js';
43
43
  export { LogoInverted } from './components/logo/LogoInverted.js';
44
44
  export { LogoPositiveColor } from './components/logo/LogoPositiveColor.js';
45
45
  export { LogoPositiveBlack } from './components/logo/LogoPositiveBlack.js';
46
+ export { DirtySwanProvider, useDirtySwan, useModalTrack } from './context.js';
46
47
  export { default as Svg1Icon } from './components/icon/icons/1.js';
47
48
  export { default as Svg2Icon } from './components/icon/icons/2.js';
48
49
  export { default as Svg3Icon } from './components/icon/icons/3.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/lib/hooks/useFocusWithin.ts"],"sourcesContent":["import { useEffect } from 'react';\n\nexport const useFocusWithin = (\n ref: HTMLElement | null,\n callback: (isFocusWithin: boolean) => void\n) => {\n useEffect(() => {\n const handleOnFocusIn = () => {\n if (!ref) {\n return;\n }\n\n const hasFocus = ref?.contains(document.activeElement);\n\n callback(Boolean(hasFocus));\n };\n\n document.addEventListener('focusin', handleOnFocusIn);\n\n return () => document?.removeEventListener('focusin', handleOnFocusIn);\n }, [callback, ref]);\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEO,IAAM,cAAc,GAAG,UAC5B,GAAuB,EACvB,QAA0C,EAAA;AAE1C,IAAA,SAAS,CAAC,YAAA;AACR,QAAA,IAAM,eAAe,GAAG,YAAA;YACtB,IAAI,CAAC,GAAG,EAAE;gBACR;YACF;AAEA,YAAA,IAAM,QAAQ,GAAG,GAAG,KAAA,IAAA,IAAH,GAAG,KAAA,MAAA,GAAA,MAAA,GAAH,GAAG,CAAE,QAAQ,CAAC,QAAQ,CAAC,aAAa,CAAC;AAEtD,YAAA,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;AAC7B,QAAA,CAAC;AAED,QAAA,QAAQ,CAAC,gBAAgB,CAAC,SAAS,EAAE,eAAe,CAAC;AAErD,QAAA,OAAO,cAAM,OAAA,QAAQ,KAAA,IAAA,IAAR,QAAQ,uBAAR,QAAQ,CAAE,mBAAmB,CAAC,SAAS,EAAE,eAAe,CAAC,CAAA,CAAzD,CAAyD;AACxE,IAAA,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;AACrB;;;;"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/lib/hooks/useFocusWithin.ts"],"sourcesContent":["import { useEffect } from 'react';\n\nexport const useFocusWithin = (\n ref: HTMLElement | null,\n callback: (isFocusWithin: boolean) => void\n) => {\n useEffect(() => {\n const handleOnFocusIn = () => {\n if (!ref) {\n return;\n }\n\n const hasFocus = ref?.contains(document.activeElement);\n\n callback(Boolean(hasFocus));\n };\n\n document.addEventListener('focusin', handleOnFocusIn);\n\n return () => document?.removeEventListener('focusin', handleOnFocusIn);\n }, [callback, ref]);\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEO,IAAM,cAAc,GAAG,UAC5B,GAAuB,EACvB,QAA0C,EAAA;AAE1C,IAAA,SAAS,CAAC,YAAA;AACR,QAAA,IAAM,eAAe,GAAG,YAAA;YACtB,IAAI,CAAC,GAAG,EAAE;gBACR;YACF;AAEA,YAAA,IAAM,QAAQ,GAAG,GAAG,KAAA,IAAA,IAAH,GAAG,KAAA,MAAA,GAAA,MAAA,GAAH,GAAG,CAAE,QAAQ,CAAC,QAAQ,CAAC,aAAa,CAAC;AAEtD,YAAA,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;AAC7B,QAAA,CAAC;AAED,QAAA,QAAQ,CAAC,gBAAgB,CAAC,SAAS,EAAE,eAAe,CAAC;AAErD,QAAA,OAAO,cAAM,OAAA,QAAQ,KAAA,IAAA,IAAR,QAAQ,uBAAR,QAAQ,CAAE,mBAAmB,CAAC,SAAS,EAAE,eAAe,CAAC,CAAA,CAAzD,CAAyD;AACxE,IAAA,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;AACrB;;;;"}
@@ -0,0 +1,13 @@
1
+ import { ReactNode } from 'react';
2
+ interface DirtySwanContextValue {
3
+ onModalOpen: () => void;
4
+ onModalClose: () => void;
5
+ }
6
+ interface DirtySwanProviderProps {
7
+ children: ReactNode;
8
+ onModalChange?: (isModalOpen: boolean) => void;
9
+ }
10
+ export declare const DirtySwanProvider: ({ children, onModalChange, }: DirtySwanProviderProps) => JSX.Element;
11
+ export declare const useDirtySwan: () => DirtySwanContextValue | null;
12
+ export declare const useModalTrack: (isVisible: boolean) => void;
13
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -41,7 +41,8 @@ import { LogoInvertedColor } from './components/logo/LogoInvertedColor';
41
41
  import { LogoInverted } from './components/logo/LogoInverted';
42
42
  import { LogoPositiveColor } from './components/logo/LogoPositiveColor';
43
43
  import { LogoPositiveBlack } from './components/logo/LogoPositiveBlack';
44
+ import { DirtySwanProvider, useDirtySwan, useModalTrack } from './context';
44
45
  export * from './components/icon';
45
- export { Accordion, DateSelector, SignaturePad, AutocompleteAddress, Input, MultiDropzone, DownloadButton, IbanInput, BottomModal, RegularModal, BottomOrRegularModal, FullScreenModal, InfoCard, Card, CardButton, Button, CurrencyInput, AutoSuggestMultiSelect, Chip, AutoSuggestInput, ComparisonTable, TableRating, TableTrueFalse, TableRowHeader, TableButton, TableInfoButton, SearchableDropdown, SegmentedControl, Checkbox, Radio, Link, InformationBox, Badge, images, illustrations, Spinner, Table, Tabs, Toggle, Toaster, toast, useEscapeKey, useFocusWithin, useOnClickOutside, useMediaQuery, LogoColor, LogoWhite, LogoBlack, LogoOrangeBox, LogoInvertedColor, LogoInverted, LogoPositiveColor, LogoPositiveBlack, };
46
+ export { Accordion, DateSelector, SignaturePad, AutocompleteAddress, Input, MultiDropzone, DownloadButton, IbanInput, BottomModal, RegularModal, BottomOrRegularModal, FullScreenModal, InfoCard, Card, CardButton, Button, CurrencyInput, AutoSuggestMultiSelect, Chip, AutoSuggestInput, ComparisonTable, TableRating, TableTrueFalse, TableRowHeader, TableButton, TableInfoButton, SearchableDropdown, SegmentedControl, Checkbox, Radio, Link, InformationBox, Badge, images, illustrations, Spinner, Table, Tabs, Toggle, Toaster, toast, useEscapeKey, useFocusWithin, useOnClickOutside, useMediaQuery, LogoColor, LogoWhite, LogoBlack, LogoOrangeBox, LogoInvertedColor, LogoInverted, LogoPositiveColor, LogoPositiveBlack, DirtySwanProvider, useDirtySwan, useModalTrack, };
46
47
  export type { AccordionProps, SearchableDropdownProps, SearchableDropdownOption, BadgeProps, IllustrationKeys, InformationBoxProps, InputProps, FileType, MultiDropzoneProps, TableHeader, UploadedFile, UploadStatus, CardProps, IconWrapperProps, TabItem, TabsProps, TableData, TableProps, };
47
48
  export type { DownloadStatus } from './models/download';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@popsure/dirty-swan",
3
- "version": "0.66.24",
3
+ "version": "0.66.25",
4
4
  "author": "Vincent Audoire <vincent@getpopsure.com>",
5
5
  "license": "MIT",
6
6
  "private": false,
package/src/index.tsx CHANGED
@@ -54,6 +54,9 @@ export {
54
54
  LogoPositiveColor,
55
55
  LogoPositiveBlack,
56
56
  SearchableDropdown,
57
+ DirtySwanProvider,
58
+ useDirtySwan,
59
+ useModalTrack,
57
60
  } from './lib';
58
61
 
59
62
  export * from './lib/components/icon';
@@ -1,5 +1,6 @@
1
1
  import { Props } from '..';
2
2
  import useOnClose, { OnCloseReturn } from '../hooks/useOnClose';
3
+ import { useModalTrack } from '../../../context';
3
4
 
4
5
  import styles from './style.module.scss';
5
6
  import classNamesUtil from 'classnames';
@@ -156,6 +157,7 @@ const InnerModal = ({
156
157
  export const GenericModal = (props: GenericModalProps) => {
157
158
  const { isOpen, onClose, dismissible = true } = props;
158
159
  const { isVisible, ...rest } = useOnClose(onClose, isOpen, dismissible);
160
+ useModalTrack(isVisible);
159
161
 
160
162
  if (!isVisible) {
161
163
  return null;
@@ -0,0 +1,136 @@
1
+ import { useState } from 'react';
2
+ import { DirtySwanProvider } from './context';
3
+ import { BottomOrRegularModal } from './components/modal';
4
+ import { Button } from './components/button';
5
+
6
+ const story = {
7
+ title: 'JSX/DirtySwanProvider',
8
+ component: DirtySwanProvider,
9
+ argTypes: {
10
+ onModalChange: {
11
+ description:
12
+ 'Callback fired when the modal open state changes. Receives `true` when the first modal opens and `false` when the last modal closes.',
13
+ action: true,
14
+ table: {
15
+ category: 'Callbacks',
16
+ },
17
+ },
18
+ },
19
+ parameters: {
20
+ docs: {
21
+ description: {
22
+ component:
23
+ 'An optional context provider that notifies consumers when any modal is open or closed. It tracks nested modals using a counter — `onModalChange(true)` fires when the first modal opens, and `onModalChange(false)` only fires when the last one closes.',
24
+ },
25
+ },
26
+ },
27
+ };
28
+
29
+ export const SingleModal = {
30
+ render: ({ onModalChange }: { onModalChange: (isOpen: boolean) => void }) => {
31
+ const [isOpen, setIsOpen] = useState(false);
32
+ const [isModalOpen, setIsModalOpen] = useState(false);
33
+
34
+ const handleModalChange = (open: boolean) => {
35
+ setIsModalOpen(open);
36
+ onModalChange(open);
37
+ };
38
+
39
+ return (
40
+ <DirtySwanProvider onModalChange={handleModalChange}>
41
+ <div className="d-flex fd-column gap16">
42
+ <div>
43
+ Modal state: <strong>{isModalOpen ? 'open' : 'closed'}</strong>
44
+ </div>
45
+ <Button className="wmn2" onClick={() => setIsOpen(true)}>
46
+ Open modal
47
+ </Button>
48
+ </div>
49
+
50
+ <BottomOrRegularModal
51
+ title="Single modal"
52
+ isOpen={isOpen}
53
+ onClose={() => setIsOpen(false)}
54
+ >
55
+ <div style={{ padding: '0 24px 24px 24px' }}>
56
+ Opening this modal triggered onModalChange(true). Closing it will
57
+ trigger onModalChange(false).
58
+ <Button className="mt24 wmn3" onClick={() => setIsOpen(false)}>
59
+ Close
60
+ </Button>
61
+ </div>
62
+ </BottomOrRegularModal>
63
+ </DirtySwanProvider>
64
+ );
65
+ },
66
+ name: 'Single modal',
67
+ };
68
+
69
+ export const NestedModals = {
70
+ render: ({ onModalChange }: { onModalChange: (isOpen: boolean) => void }) => {
71
+ const [isFirstOpen, setIsFirstOpen] = useState(false);
72
+ const [isSecondOpen, setIsSecondOpen] = useState(false);
73
+ const [isModalOpen, setIsModalOpen] = useState(false);
74
+
75
+ const handleModalChange = (open: boolean) => {
76
+ setIsModalOpen(open);
77
+ onModalChange(open);
78
+ };
79
+
80
+ return (
81
+ <DirtySwanProvider onModalChange={handleModalChange}>
82
+ <div className="d-flex fd-column gap16">
83
+ <div>
84
+ Modal state: <strong>{isModalOpen ? 'open' : 'closed'}</strong>
85
+ </div>
86
+ <Button className="wmn2" onClick={() => setIsFirstOpen(true)}>
87
+ Open first modal
88
+ </Button>
89
+ </div>
90
+
91
+ <BottomOrRegularModal
92
+ title="First modal"
93
+ isOpen={isFirstOpen}
94
+ onClose={() => setIsFirstOpen(false)}
95
+ >
96
+ <div style={{ padding: '0 24px 24px 24px' }}>
97
+ This is the first modal. Open a second one to see that
98
+ onModalChange(false) only fires when both are closed.
99
+ <div className="d-flex fd-row gap8 mt24">
100
+ <Button className="wmn3" onClick={() => setIsSecondOpen(true)}>
101
+ Open second modal
102
+ </Button>
103
+ <Button
104
+ variant="textBlack"
105
+ className="wmn3"
106
+ onClick={() => setIsFirstOpen(false)}
107
+ >
108
+ Close
109
+ </Button>
110
+ </div>
111
+ </div>
112
+ </BottomOrRegularModal>
113
+
114
+ <BottomOrRegularModal
115
+ title="Second modal"
116
+ isOpen={isSecondOpen}
117
+ onClose={() => setIsSecondOpen(false)}
118
+ >
119
+ <div style={{ padding: '0 24px 24px 24px' }}>
120
+ This is the second (nested) modal. Closing this one will not trigger
121
+ onModalChange(false) because the first modal is still open.
122
+ <Button
123
+ className="mt24 wmn3"
124
+ onClick={() => setIsSecondOpen(false)}
125
+ >
126
+ Close
127
+ </Button>
128
+ </div>
129
+ </BottomOrRegularModal>
130
+ </DirtySwanProvider>
131
+ );
132
+ },
133
+ name: 'Nested modals',
134
+ };
135
+
136
+ export default story;
@@ -0,0 +1,88 @@
1
+ import { render, screen, fireEvent } from './util/testUtils';
2
+ import { DirtySwanProvider, useDirtySwan } from './context';
3
+
4
+ const TestConsumer = () => {
5
+ const ctx = useDirtySwan();
6
+
7
+ return (
8
+ <div>
9
+ <button onClick={() => ctx?.onModalOpen()}>open</button>
10
+ <button onClick={() => ctx?.onModalClose()}>close</button>
11
+ </div>
12
+ );
13
+ };
14
+
15
+ describe('DirtySwanProvider', () => {
16
+ it('calls onModalChange(true) when first modal opens', () => {
17
+ const onModalChange = jest.fn();
18
+ render(
19
+ <DirtySwanProvider onModalChange={onModalChange}>
20
+ <TestConsumer />
21
+ </DirtySwanProvider>
22
+ );
23
+
24
+ fireEvent.click(screen.getByText('open'));
25
+
26
+ expect(onModalChange).toHaveBeenCalledWith(true);
27
+ });
28
+
29
+ it('calls onModalChange(false) when last modal closes', () => {
30
+ const onModalChange = jest.fn();
31
+ render(
32
+ <DirtySwanProvider onModalChange={onModalChange}>
33
+ <TestConsumer />
34
+ </DirtySwanProvider>
35
+ );
36
+
37
+ fireEvent.click(screen.getByText('open'));
38
+ fireEvent.click(screen.getByText('close'));
39
+
40
+ expect(onModalChange).toHaveBeenLastCalledWith(false);
41
+ });
42
+
43
+ it('does not call onModalChange(false) when nested modals are still open', () => {
44
+ const onModalChange = jest.fn();
45
+ render(
46
+ <DirtySwanProvider onModalChange={onModalChange}>
47
+ <TestConsumer />
48
+ </DirtySwanProvider>
49
+ );
50
+
51
+ fireEvent.click(screen.getByText('open'));
52
+ fireEvent.click(screen.getByText('open'));
53
+ fireEvent.click(screen.getByText('close'));
54
+
55
+ expect(onModalChange).toHaveBeenCalledTimes(1);
56
+ expect(onModalChange).toHaveBeenCalledWith(true);
57
+ });
58
+
59
+ it('calls onModalChange(false) when all nested modals close', () => {
60
+ const onModalChange = jest.fn();
61
+ render(
62
+ <DirtySwanProvider onModalChange={onModalChange}>
63
+ <TestConsumer />
64
+ </DirtySwanProvider>
65
+ );
66
+
67
+ fireEvent.click(screen.getByText('open'));
68
+ fireEvent.click(screen.getByText('open'));
69
+ fireEvent.click(screen.getByText('close'));
70
+ fireEvent.click(screen.getByText('close'));
71
+
72
+ expect(onModalChange).toHaveBeenCalledTimes(2);
73
+ expect(onModalChange).toHaveBeenLastCalledWith(false);
74
+ });
75
+
76
+ it('returns null when used outside provider', () => {
77
+ let contextValue: ReturnType<typeof useDirtySwan> = undefined as any;
78
+
79
+ const Spy = () => {
80
+ contextValue = useDirtySwan();
81
+ return null;
82
+ };
83
+
84
+ render(<Spy />);
85
+
86
+ expect(contextValue).toBeNull();
87
+ });
88
+ });