@openedx/paragon 23.18.2 → 23.19.1

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.
package/README.md CHANGED
@@ -514,10 +514,34 @@ Paragon components can have different behavior in the MFE environment. `example`
514
514
 
515
515
  Steps to install the `example` app.
516
516
 
517
+ To set up the example app with a local Tutor installation, you'll need to configure a Tutor plugin to handle CORS settings
518
+
519
+ Prerequisites.
520
+
521
+ Before running the example app, ensure you have a local Tutor installation configured.
522
+ 1. Create a new file (i.e. `paragon-example-app.py`) in your tutor plugins folder (`tutor plugins printroot` to get the path).
523
+ 2. Add the following content to the paragon-example-app.py file:
524
+ ```
525
+ from tutor import hooks
526
+
527
+ hooks.Filters.ENV_PATCHES.add_item(
528
+ (
529
+ "openedx-lms-development-settings",
530
+ """
531
+ CORS_ORIGIN_WHITELIST.append("http://localhost:8080")
532
+ LOGIN_REDIRECT_WHITELIST.append("http://localhost:8080")
533
+ CSRF_TRUSTED_ORIGINS.append("http://localhost:8080")
534
+ """
535
+ )
536
+ )
537
+ ```
538
+ 3. Enable the plugin: `tutor plugins enable paragon-example-app`
539
+ 4. Apply configuration changes: `tutor config save` && `tutor dev restart`
540
+
541
+ Running the Example App:
517
542
  1. `npm install` to install dependencies.
518
- 2. Launch any devstack. It is required for MFE to login into the system and set up configs.
519
- 3. `npm run start-example-mfe` to start the app.
520
- 4. Go to the `example/src/MyComponent.jsx` and use Paragon components inside the MFE environment.
543
+ 2. `npm run example:start` to start the app.
544
+ 3. Go to the `example/src/MyComponent.jsx` and use Paragon components inside the MFE environment.
521
545
 
522
546
  ## Semantic Release
523
547
 
@@ -16,7 +16,7 @@ interface BreadcrumbProps {
16
16
  spacer?: React.ReactElement;
17
17
  /** allows to add a custom function to be called `onClick` of a breadcrumb link.
18
18
  * The use case for this is for adding custom analytics to the component. */
19
- clickHandler?: (event: React.MouseEvent, link: any) => void;
19
+ clickHandler?: (event: React.MouseEvent<HTMLAnchorElement>, link: any) => void;
20
20
  /** The `Breadcrumbs` style variant to use. */
21
21
  variant?: 'light' | 'dark';
22
22
  /** The `Breadcrumbs` mobile variant view. */
@@ -25,6 +25,8 @@ interface BreadcrumbProps {
25
25
  * [react-router's Link](https://reactrouter.com/en/main/components/link).
26
26
  */
27
27
  linkAs?: React.ElementType;
28
+ /** Optional class name(s) to append to the base `<nav>` element. */
29
+ className?: string;
28
30
  }
29
- declare function Breadcrumb({ links, activeLabel, spacer, clickHandler, variant, isMobile, ariaLabel, linkAs, ...props }: BreadcrumbProps): import("react/jsx-runtime").JSX.Element;
31
+ declare function Breadcrumb({ links, activeLabel, spacer, clickHandler, className, variant, isMobile, ariaLabel, linkAs, }: BreadcrumbProps): import("react/jsx-runtime").JSX.Element;
30
32
  export default Breadcrumb;
@@ -8,18 +8,17 @@ function Breadcrumb({
8
8
  activeLabel,
9
9
  spacer,
10
10
  clickHandler,
11
+ className,
11
12
  variant = 'light',
12
13
  isMobile = false,
13
14
  ariaLabel = 'breadcrumb',
14
- linkAs = 'a',
15
- ...props
15
+ linkAs = 'a'
16
16
  }) {
17
17
  const linkCount = links.length;
18
18
  const displayLinks = isMobile ? [links[linkCount - 1]] : links;
19
19
  return /*#__PURE__*/React.createElement("nav", {
20
20
  "aria-label": ariaLabel,
21
- className: classNames('pgn__breadcrumb', `pgn__breadcrumb-${variant}`),
22
- ...props
21
+ className: classNames('pgn__breadcrumb', `pgn__breadcrumb-${variant}`, className)
23
22
  }, /*#__PURE__*/React.createElement("ol", {
24
23
  className: classNames('list-inline', {
25
24
  'is-mobile': isMobile
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["React","classNames","BreadcrumbLink","ChevronRight","Icon","Breadcrumb","links","activeLabel","spacer","clickHandler","variant","isMobile","ariaLabel","linkAs","props","linkCount","length","displayLinks","createElement","className","map","link","i","Fragment","key","label","as","linkProps","role","src","id"],"sources":["../../src/Breadcrumb/index.tsx"],"sourcesContent":["import React from 'react';\nimport classNames from 'classnames';\n\nimport BreadcrumbLink from './BreadcrumbLink';\nimport { ChevronRight } from '../../icons';\nimport Icon from '../Icon';\n\ninterface BreadcrumbProps {\n /** An array of objects describing links to be rendered. The contents of an object depend on the value of `linkAs`\n * prop as these objects will get passed down as props to the underlying component defined by `linkAs` prop.\n */\n links: Array<{ label: string, [key: string]: any }>;\n /** allows to add a label that is not a link to the end of the breadcrumb. */\n activeLabel?: string;\n /** label of the element */\n ariaLabel?: string;\n /** allows to add a custom element between the breadcrumb items.\n * Defaults to `>` rendered using the `Icon` component. */\n spacer?: React.ReactElement;\n /** allows to add a custom function to be called `onClick` of a breadcrumb link.\n * The use case for this is for adding custom analytics to the component. */\n clickHandler?: (event: React.MouseEvent, link: any) => void;\n /** The `Breadcrumbs` style variant to use. */\n variant?: 'light' | 'dark';\n /** The `Breadcrumbs` mobile variant view. */\n isMobile?: boolean;\n /** Specifies the base element to use when rendering links, you should generally use either plain 'a' or\n * [react-router's Link](https://reactrouter.com/en/main/components/link).\n */\n linkAs?: React.ElementType;\n}\n\nfunction Breadcrumb({\n links, activeLabel, spacer, clickHandler,\n variant = 'light', isMobile = false, ariaLabel = 'breadcrumb', linkAs = 'a', ...props\n}: BreadcrumbProps) {\n const linkCount = links.length;\n const displayLinks = isMobile ? [links[linkCount - 1]] : links;\n\n return (\n <nav\n aria-label={ariaLabel}\n className={classNames('pgn__breadcrumb', `pgn__breadcrumb-${variant}`)}\n {...props}\n >\n <ol className={classNames('list-inline', { 'is-mobile': isMobile })}>\n {displayLinks.map((link, i) => (\n <React.Fragment key={link.label}>\n <li className={classNames('list-inline-item')}>\n <BreadcrumbLink as={linkAs} clickHandler={clickHandler} linkProps={link} />\n </li>\n {(activeLabel || ((i + 1) < linkCount))\n && (\n <li className=\"list-inline-item\" role=\"presentation\">\n {spacer || <Icon src={ChevronRight} id={`spacer-${i}`} />}\n </li>\n )}\n </React.Fragment>\n ))}\n {!isMobile && activeLabel && <li className=\"list-inline-item active\" key=\"active\" aria-current=\"page\">{activeLabel}</li>}\n </ol>\n </nav>\n );\n}\n\nexport default Breadcrumb;\n"],"mappings":"AAAA,OAAOA,KAAK,MAAM,OAAO;AACzB,OAAOC,UAAU,MAAM,YAAY;AAEnC,OAAOC,cAAc,MAAM,kBAAkB;AAC7C,SAASC,YAAY,QAAQ,aAAa;AAC1C,OAAOC,IAAI,MAAM,SAAS;AA2B1B,SAASC,UAAUA,CAAC;EAClBC,KAAK;EAAEC,WAAW;EAAEC,MAAM;EAAEC,YAAY;EACxCC,OAAO,GAAG,OAAO;EAAEC,QAAQ,GAAG,KAAK;EAAEC,SAAS,GAAG,YAAY;EAAEC,MAAM,GAAG,GAAG;EAAE,GAAGC;AACjE,CAAC,EAAE;EAClB,MAAMC,SAAS,GAAGT,KAAK,CAACU,MAAM;EAC9B,MAAMC,YAAY,GAAGN,QAAQ,GAAG,CAACL,KAAK,CAACS,SAAS,GAAG,CAAC,CAAC,CAAC,GAAGT,KAAK;EAE9D,oBACEN,KAAA,CAAAkB,aAAA;IACE,cAAYN,SAAU;IACtBO,SAAS,EAAElB,UAAU,CAAC,iBAAiB,EAAE,mBAAmBS,OAAO,EAAE,CAAE;IAAA,GACnEI;EAAK,gBAETd,KAAA,CAAAkB,aAAA;IAAIC,SAAS,EAAElB,UAAU,CAAC,aAAa,EAAE;MAAE,WAAW,EAAEU;IAAS,CAAC;EAAE,GACjEM,YAAY,CAACG,GAAG,CAAC,CAACC,IAAI,EAAEC,CAAC,kBACxBtB,KAAA,CAAAkB,aAAA,CAAClB,KAAK,CAACuB,QAAQ;IAACC,GAAG,EAAEH,IAAI,CAACI;EAAM,gBAC9BzB,KAAA,CAAAkB,aAAA;IAAIC,SAAS,EAAElB,UAAU,CAAC,kBAAkB;EAAE,gBAC5CD,KAAA,CAAAkB,aAAA,CAAChB,cAAc;IAACwB,EAAE,EAAEb,MAAO;IAACJ,YAAY,EAAEA,YAAa;IAACkB,SAAS,EAAEN;EAAK,CAAE,CACxE,CAAC,EACJ,CAACd,WAAW,IAAMe,CAAC,GAAG,CAAC,GAAIP,SAAU,kBAEpCf,KAAA,CAAAkB,aAAA;IAAIC,SAAS,EAAC,kBAAkB;IAACS,IAAI,EAAC;EAAc,GACjDpB,MAAM,iBAAIR,KAAA,CAAAkB,aAAA,CAACd,IAAI;IAACyB,GAAG,EAAE1B,YAAa;IAAC2B,EAAE,EAAE,UAAUR,CAAC;EAAG,CAAE,CACtD,CAEQ,CACjB,CAAC,EACD,CAACX,QAAQ,IAAIJ,WAAW,iBAAIP,KAAA,CAAAkB,aAAA;IAAIC,SAAS,EAAC,yBAAyB;IAACK,GAAG,EAAC,QAAQ;IAAC,gBAAa;EAAM,GAAEjB,WAAgB,CACrH,CACD,CAAC;AAEV;AAEA,eAAeF,UAAU","ignoreList":[]}
1
+ {"version":3,"file":"index.js","names":["React","classNames","BreadcrumbLink","ChevronRight","Icon","Breadcrumb","links","activeLabel","spacer","clickHandler","className","variant","isMobile","ariaLabel","linkAs","linkCount","length","displayLinks","createElement","map","link","i","Fragment","key","label","as","linkProps","role","src","id"],"sources":["../../src/Breadcrumb/index.tsx"],"sourcesContent":["import React from 'react';\nimport classNames from 'classnames';\n\nimport BreadcrumbLink from './BreadcrumbLink';\nimport { ChevronRight } from '../../icons';\nimport Icon from '../Icon';\n\ninterface BreadcrumbProps {\n /** An array of objects describing links to be rendered. The contents of an object depend on the value of `linkAs`\n * prop as these objects will get passed down as props to the underlying component defined by `linkAs` prop.\n */\n links: Array<{ label: string, [key: string]: any }>;\n /** allows to add a label that is not a link to the end of the breadcrumb. */\n activeLabel?: string;\n /** label of the element */\n ariaLabel?: string;\n /** allows to add a custom element between the breadcrumb items.\n * Defaults to `>` rendered using the `Icon` component. */\n spacer?: React.ReactElement;\n /** allows to add a custom function to be called `onClick` of a breadcrumb link.\n * The use case for this is for adding custom analytics to the component. */\n clickHandler?: (event: React.MouseEvent<HTMLAnchorElement>, link: any) => void;\n /** The `Breadcrumbs` style variant to use. */\n variant?: 'light' | 'dark';\n /** The `Breadcrumbs` mobile variant view. */\n isMobile?: boolean;\n /** Specifies the base element to use when rendering links, you should generally use either plain 'a' or\n * [react-router's Link](https://reactrouter.com/en/main/components/link).\n */\n linkAs?: React.ElementType;\n /** Optional class name(s) to append to the base `<nav>` element. */\n className?: string;\n}\n\nfunction Breadcrumb({\n links, activeLabel, spacer, clickHandler, className,\n variant = 'light', isMobile = false, ariaLabel = 'breadcrumb', linkAs = 'a',\n}: BreadcrumbProps) {\n const linkCount = links.length;\n const displayLinks = isMobile ? [links[linkCount - 1]] : links;\n\n return (\n <nav\n aria-label={ariaLabel}\n className={classNames('pgn__breadcrumb', `pgn__breadcrumb-${variant}`, className)}\n >\n <ol className={classNames('list-inline', { 'is-mobile': isMobile })}>\n {displayLinks.map((link, i) => (\n <React.Fragment key={link.label}>\n <li className={classNames('list-inline-item')}>\n <BreadcrumbLink as={linkAs} clickHandler={clickHandler} linkProps={link} />\n </li>\n {(activeLabel || ((i + 1) < linkCount))\n && (\n <li className=\"list-inline-item\" role=\"presentation\">\n {spacer || <Icon src={ChevronRight} id={`spacer-${i}`} />}\n </li>\n )}\n </React.Fragment>\n ))}\n {!isMobile && activeLabel && <li className=\"list-inline-item active\" key=\"active\" aria-current=\"page\">{activeLabel}</li>}\n </ol>\n </nav>\n );\n}\n\nexport default Breadcrumb;\n"],"mappings":"AAAA,OAAOA,KAAK,MAAM,OAAO;AACzB,OAAOC,UAAU,MAAM,YAAY;AAEnC,OAAOC,cAAc,MAAM,kBAAkB;AAC7C,SAASC,YAAY,QAAQ,aAAa;AAC1C,OAAOC,IAAI,MAAM,SAAS;AA6B1B,SAASC,UAAUA,CAAC;EAClBC,KAAK;EAAEC,WAAW;EAAEC,MAAM;EAAEC,YAAY;EAAEC,SAAS;EACnDC,OAAO,GAAG,OAAO;EAAEC,QAAQ,GAAG,KAAK;EAAEC,SAAS,GAAG,YAAY;EAAEC,MAAM,GAAG;AACzD,CAAC,EAAE;EAClB,MAAMC,SAAS,GAAGT,KAAK,CAACU,MAAM;EAC9B,MAAMC,YAAY,GAAGL,QAAQ,GAAG,CAACN,KAAK,CAACS,SAAS,GAAG,CAAC,CAAC,CAAC,GAAGT,KAAK;EAE9D,oBACEN,KAAA,CAAAkB,aAAA;IACE,cAAYL,SAAU;IACtBH,SAAS,EAAET,UAAU,CAAC,iBAAiB,EAAE,mBAAmBU,OAAO,EAAE,EAAED,SAAS;EAAE,gBAElFV,KAAA,CAAAkB,aAAA;IAAIR,SAAS,EAAET,UAAU,CAAC,aAAa,EAAE;MAAE,WAAW,EAAEW;IAAS,CAAC;EAAE,GACjEK,YAAY,CAACE,GAAG,CAAC,CAACC,IAAI,EAAEC,CAAC,kBACxBrB,KAAA,CAAAkB,aAAA,CAAClB,KAAK,CAACsB,QAAQ;IAACC,GAAG,EAAEH,IAAI,CAACI;EAAM,gBAC9BxB,KAAA,CAAAkB,aAAA;IAAIR,SAAS,EAAET,UAAU,CAAC,kBAAkB;EAAE,gBAC5CD,KAAA,CAAAkB,aAAA,CAAChB,cAAc;IAACuB,EAAE,EAAEX,MAAO;IAACL,YAAY,EAAEA,YAAa;IAACiB,SAAS,EAAEN;EAAK,CAAE,CACxE,CAAC,EACJ,CAACb,WAAW,IAAMc,CAAC,GAAG,CAAC,GAAIN,SAAU,kBAEpCf,KAAA,CAAAkB,aAAA;IAAIR,SAAS,EAAC,kBAAkB;IAACiB,IAAI,EAAC;EAAc,GACjDnB,MAAM,iBAAIR,KAAA,CAAAkB,aAAA,CAACd,IAAI;IAACwB,GAAG,EAAEzB,YAAa;IAAC0B,EAAE,EAAE,UAAUR,CAAC;EAAG,CAAE,CACtD,CAEQ,CACjB,CAAC,EACD,CAACT,QAAQ,IAAIL,WAAW,iBAAIP,KAAA,CAAAkB,aAAA;IAAIR,SAAS,EAAC,yBAAyB;IAACa,GAAG,EAAC,QAAQ;IAAC,gBAAa;EAAM,GAAEhB,WAAgB,CACrH,CACD,CAAC;AAEV;AAEA,eAAeF,UAAU","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openedx/paragon",
3
- "version": "23.18.2",
3
+ "version": "23.19.1",
4
4
  "description": "Accessible, responsive UI component library based on Bootstrap.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -95,4 +95,29 @@ describe('<Breadcrumb />', () => {
95
95
  expect(links.getAttribute('target')).toBe('_blank');
96
96
  expect(links.getAttribute('href')).toBe('/link-1');
97
97
  });
98
+
99
+ it('renders with a custom CSS class', () => {
100
+ render(<Breadcrumb {...baseProps} ariaLabel="Breadcrumbs" className="foobar" />);
101
+ const nav = screen.getByLabelText('Breadcrumbs');
102
+ expect(nav.classList).toContain('foobar');
103
+ expect(nav.classList).toContain('pgn__breadcrumb');
104
+ expect(nav.classList).toContain('pgn__breadcrumb-light');
105
+ });
106
+
107
+ it('can access data-xxxxx attributes on the links in clickHandler', async () => {
108
+ const user = userEvent.setup();
109
+ const clickHandler = jest.fn();
110
+ render(
111
+ <Breadcrumb
112
+ ariaLabel="Location"
113
+ links={[
114
+ { label: 'Link1', href: '/link1', 'data-parent-index': 17 },
115
+ ]}
116
+ clickHandler={({ currentTarget }) => clickHandler(currentTarget.dataset.parentIndex)}
117
+ />,
118
+ );
119
+ const link = screen.getByRole('link', { name: 'Link1' });
120
+ await user.click(link);
121
+ expect(clickHandler).toHaveBeenCalledWith('17');
122
+ });
98
123
  });
@@ -19,7 +19,7 @@ interface BreadcrumbProps {
19
19
  spacer?: React.ReactElement;
20
20
  /** allows to add a custom function to be called `onClick` of a breadcrumb link.
21
21
  * The use case for this is for adding custom analytics to the component. */
22
- clickHandler?: (event: React.MouseEvent, link: any) => void;
22
+ clickHandler?: (event: React.MouseEvent<HTMLAnchorElement>, link: any) => void;
23
23
  /** The `Breadcrumbs` style variant to use. */
24
24
  variant?: 'light' | 'dark';
25
25
  /** The `Breadcrumbs` mobile variant view. */
@@ -28,11 +28,13 @@ interface BreadcrumbProps {
28
28
  * [react-router's Link](https://reactrouter.com/en/main/components/link).
29
29
  */
30
30
  linkAs?: React.ElementType;
31
+ /** Optional class name(s) to append to the base `<nav>` element. */
32
+ className?: string;
31
33
  }
32
34
 
33
35
  function Breadcrumb({
34
- links, activeLabel, spacer, clickHandler,
35
- variant = 'light', isMobile = false, ariaLabel = 'breadcrumb', linkAs = 'a', ...props
36
+ links, activeLabel, spacer, clickHandler, className,
37
+ variant = 'light', isMobile = false, ariaLabel = 'breadcrumb', linkAs = 'a',
36
38
  }: BreadcrumbProps) {
37
39
  const linkCount = links.length;
38
40
  const displayLinks = isMobile ? [links[linkCount - 1]] : links;
@@ -40,8 +42,7 @@ function Breadcrumb({
40
42
  return (
41
43
  <nav
42
44
  aria-label={ariaLabel}
43
- className={classNames('pgn__breadcrumb', `pgn__breadcrumb-${variant}`)}
44
- {...props}
45
+ className={classNames('pgn__breadcrumb', `pgn__breadcrumb-${variant}`, className)}
45
46
  >
46
47
  <ol className={classNames('list-inline', { 'is-mobile': isMobile })}>
47
48
  {displayLinks.map((link, i) => (