diff --git a/.yarn/offline-mirror/cssfontparser-1.2.1.tgz b/.yarn/offline-mirror/cssfontparser-1.2.1.tgz deleted file mode 100644 index 4844dc1c5..000000000 Binary files a/.yarn/offline-mirror/cssfontparser-1.2.1.tgz and /dev/null differ diff --git a/.yarn/offline-mirror/jest-canvas-mock-2.1.2.tgz b/.yarn/offline-mirror/jest-canvas-mock-2.1.2.tgz deleted file mode 100644 index 1fd077c7d..000000000 Binary files a/.yarn/offline-mirror/jest-canvas-mock-2.1.2.tgz and /dev/null differ diff --git a/README.md b/README.md index 200fd338d..35d95e82a 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ To install Carbon for IBM Security in your project, you'll need to run one of th ```bash # npm - https://www.npmjs.com -npm i -S @carbon/ibm-security +npm i @carbon/ibm-security # Yarn - https://yarnpkg.com yarn add @carbon/ibm-security @@ -44,7 +44,7 @@ yarn add @carbon/ibm-security ### Distribution tags -Please use [distribution tags](https://docs.npmjs.com/cli/dist-tag) to install the most relevant version of this framework. e.g. `npm i -S @carbon/ibm-security@latest`: +Please use [distribution tags](https://docs.npmjs.com/cli/dist-tag) to install the most relevant version of this library. e.g. `npm i @carbon/ibm-security@latest`: - `@latest` - Stable - `@canary` - Unstable prerelease @@ -69,10 +69,10 @@ const { ComponentName } = require('@carbon/ibm-security'); To add a component style to your build, import the component directly. Importing a component this way will bring in any dependencies that component has as well. The import system removes duplicate dependencies, so shared dependencies between components will not create extra CSS. -In addition, to resolve your `@import` declarations, you will need to setup `sass` so that `node_modules` is included in the [`includePaths`](https://github.com/sass/node-sass#includepaths) option. +In addition, to resolve your import declarations, you will need to setup `sass` so that `node_modules` is included in the [`includePaths`](https://github.com/sass/node-sass#includepaths) option. ```scss -@import '@carbon/ibm-security/scss/components/ComponentName/index'; +@use '@carbon/ibm-security/scss/components/ComponentName'; ``` #### Feature flags @@ -86,7 +86,7 @@ $security--feature-flags: ( security--css-custom-property-theming: false, ); -@import '@carbon/ibm-security/scss/components/ComponentName/index'; +@use '@carbon/ibm-security/scss/components/ComponentName'; ``` Also refer to [feature flags in Carbon](https://github.com/carbon-design-system/carbon/blob/main/packages/components/src/globals/scss/_feature-flags.scss). diff --git a/docs/migration/carbon-for-ibm-security/color.md b/docs/migration/carbon-for-ibm-security/color.md index 2db834e79..4d144c4b3 100644 --- a/docs/migration/carbon-for-ibm-security/color.md +++ b/docs/migration/carbon-for-ibm-security/color.md @@ -6,10 +6,10 @@ Also refer to [migration in Carbon](https://github.com/carbon-design-system/carb ### Imports -| `carbon-addons-security` | `@carbon/ibm-security` | -| ----------------------------------------- | ---------------------------------------------------------- | -| `import { white } from '@ibmduo/colors';` | `import { white } from '@carbon/colors';` | -| `@import '@ibmduo/colors/**/*';` | `@import '@carbon/ibm-security/scss/globals/color/index';` | +| `carbon-addons-security` | `@carbon/ibm-security` | +| ----------------------------------------- | ------------------------------------------------- | +| `import { white } from '@ibmduo/colors';` | `import { white } from '@carbon/colors';` | +| `@use '@ibmduo/colors/**/*';` | `@use '@carbon/ibm-security/scss/globals/color';` | ### Variables diff --git a/docs/migration/carbon-for-ibm-security/grid.md b/docs/migration/carbon-for-ibm-security/grid.md index e6972da53..f9b77baa8 100644 --- a/docs/migration/carbon-for-ibm-security/grid.md +++ b/docs/migration/carbon-for-ibm-security/grid.md @@ -8,9 +8,9 @@ Also refer to [migration in Carbon](https://github.com/carbon-design-system/carb ### Imports -| `carbon-addons-security` | `@carbon/ibm-security` | -| ------------------------------ | --------------------------------------------------------- | -| `@import '@ibmduo/grid/**/*';` | `@import '@carbon/ibm-security/scss/globals/grid/index';` | +| `carbon-addons-security` | `@carbon/ibm-security` | +| --------------------------- | ------------------------------------------------ | +| `@use '@ibmduo/grid/**/*';` | `@use '@carbon/ibm-security/scss/globals/grid';` | ### Feature flags diff --git a/docs/migration/carbon-for-ibm-security/layout.md b/docs/migration/carbon-for-ibm-security/layout.md index 0363d15ed..e0ae96e00 100644 --- a/docs/migration/carbon-for-ibm-security/layout.md +++ b/docs/migration/carbon-for-ibm-security/layout.md @@ -6,10 +6,10 @@ Also refer to [migration in Carbon](https://github.com/carbon-design-system/carb ### Imports -| `carbon-addons-security` | `@carbon/ibm-security` | -| ---------------------------------------------------------------------------------- | ----------------------------------------------------------- | -| `import { spacing } from '@ibm-security/carbon-addons-security/lib/scss-exports';` | `import { spacing } from '@carbon/layout';` | -| `@import '@ibmduo/grid/**/*';` | `@import '@carbon/ibm-security/scss/globals/layout/index';` | +| `carbon-addons-security` | `@carbon/ibm-security` | +| ---------------------------------------------------------------------------------- | -------------------------------------------------- | +| `import { spacing } from '@ibm-security/carbon-addons-security/lib/scss-exports';` | `import { spacing } from '@carbon/layout';` | +| `@use '@ibmduo/grid/**/*';` | `@use '@carbon/ibm-security/scss/globals/layout';` | ### Functions diff --git a/docs/migration/carbon-for-ibm-security/type.md b/docs/migration/carbon-for-ibm-security/type.md index 8eb1bc436..80e097927 100644 --- a/docs/migration/carbon-for-ibm-security/type.md +++ b/docs/migration/carbon-for-ibm-security/type.md @@ -8,9 +8,9 @@ Also refer to [migration in Carbon](https://github.com/carbon-design-system/carb ### Imports -| `carbon-addons-security` | `@carbon/ibm-security` | -| ------------------------------ | --------------------------------------------------------- | -| `@import '@ibmduo/type/**/*';` | `@import '@carbon/ibm-security/scss/globals/type/index';` | +| `carbon-addons-security` | `@carbon/ibm-security` | +| --------------------------- | ------------------------------------------------ | +| `@use '@ibmduo/type/**/*';` | `@use '@carbon/ibm-security/scss/globals/type';` | ### Classes diff --git a/docs/themes/README.md b/docs/themes/README.md index cacf92aad..bb2f383b2 100644 --- a/docs/themes/README.md +++ b/docs/themes/README.md @@ -27,7 +27,7 @@ Currently, only 2 themes are supported: **Gray 100**, activated by default, and If your project is using Sass, you can include the following in your Sass file: ```scss -@import '@carbon/ibm-security/scss/globals/theme/index'; +@use '@carbon/ibm-security/scss/globals/theme'; ``` This will include theming styles and tokens for the **Gray 100 theme _by default_**. diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 000000000..89e215b74 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,12 @@ +/** + * @file Jest configuration. + * @copyright IBM Security 2021 + */ + +module.exports = { + collectCoverage: true, + rootDir: 'src', + setupFilesAfterEnv: ['/../config/jest'], + snapshotSerializers: ['enzyme-to-json/serializer'], + testTimeout: 10000, +}; diff --git a/package.json b/package.json index 3adac2a9f..0cc5c5bd0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@carbon/ibm-security", - "version": "1.41.0", + "version": "1.42.0-prerelease.3", "description": "Carbon for IBM Security", "license": "Apache-2.0", "main": "lib/index.js", @@ -151,7 +151,6 @@ "handlebars": "^4.4.3", "husky": "^3.0.9", "jest": "^24.7.1", - "jest-canvas-mock": "^2.1.0", "lint-staged": "^9.4.2", "node-sass": "^4.7.2", "npm-run-all": "^4.1.5", @@ -191,20 +190,6 @@ "scss", "macros" ], - "jest": { - "collectCoverage": true, - "coverageDirectory": "/../coverage", - "rootDir": "src", - "watchman": false, - "setupFilesAfterEnv": [ - "/../config/jest", - "jest-canvas-mock" - ], - "snapshotSerializers": [ - "enzyme-to-json/serializer" - ], - "testURL": "http://localhost/" - }, "publishConfig": { "access": "public" }, diff --git a/src/__tests__/scss/__snapshots__/SCSS.spec.js.snap b/src/__tests__/scss/__snapshots__/SCSS.spec.js.snap index b04b1a2e5..eecd4af4d 100644 --- a/src/__tests__/scss/__snapshots__/SCSS.spec.js.snap +++ b/src/__tests__/scss/__snapshots__/SCSS.spec.js.snap @@ -11463,6 +11463,121 @@ li.bx--accordion__item--disabled:last-of-type { background-color: var(--cds-hover-ui, #353535); } +.security--layout-module--list-item { + display: flex; + padding: 1rem; + background-color: var(--cds-ui-01, #262626); + border: none; + border-top: 0.0625rem solid var(--cds-ui-03, #393939); +} + +.security--layout-module--list-item:last-of-type { + border-bottom: 0.0625rem solid var(--cds-ui-03, #393939); +} + +.security--layout-module--list-item--a, .security--layout-module--list-item--button { + transition-duration: 0.2s; + transition-property: background-color; + transition-timing-function: cubic-bezier(0.2, 0, 0.38, 0.9); + color: inherit; + text-decoration: none; + cursor: pointer; +} + +.security--layout-module--list-item--button { + font-family: inherit; + text-align: left; + width: 100%; +} + +.security--layout-module--list-item--a:focus, .security--layout-module--list-item--a:hover, .security--layout-module--list-item--button:focus, .security--layout-module--list-item--button:hover { + background-color: var(--cds-hover-ui, #353535); +} + +.security--layout-module--list-item--a:focus, .security--layout-module--list-item--button:focus { + outline: 2px solid var(--cds-focus, #ffffff); + outline-offset: -2px; +} + +@media screen and (prefers-contrast) { + .security--layout-module--list-item--a:focus, .security--layout-module--list-item--button:focus { + outline-style: dotted; + } +} + +.security--layout-module--list-item__farside-column { + margin-left: auto; +} + +.security--layout-module--list-item__title, .security--layout-module--list-item__description { + margin-top: 0; +} + +.security--layout-module--list-item__icon { + margin-right: 1rem; + margin-bottom: 1rem; +} + +.security--layout-module--list-item__avatar { + margin-top: 0.25rem; + margin-right: 0.75rem; +} + +.security--layout-module--list-item__avatar:first-child { + margin-top: 0; +} + +.security--layout-module--list-item__profile-image { + margin-right: 0.75rem; + height: 24px; + width: 24px; +} + +.security--layout-module--list-item__title { + font-size: var(--cds-body-short-01-font-size, 0.875rem); + font-weight: var(--cds-body-short-01-font-weight, 400); + line-height: var(--cds-body-short-01-line-height, 1.29); + letter-spacing: var(--cds-body-short-01-letter-spacing, 0.16px); +} + +.security--layout-module--list-item__description, .security--layout-module--list-item__label { + color: var(--cds-text-02, #c6c6c6); +} + +.security--layout-module--list-item__description { + font-size: var(--cds-body-short-01-font-size, 0.875rem); + font-weight: var(--cds-body-short-01-font-weight, 400); + line-height: var(--cds-body-short-01-line-height, 1.29); + letter-spacing: var(--cds-body-short-01-letter-spacing, 0.16px); +} + +.security--layout-module--list-item__label { + font-size: var(--cds-label-01-font-size, 0.75rem); + font-weight: var(--cds-label-01-font-weight, 400); + line-height: var(--cds-label-01-line-height, 1.34); + letter-spacing: var(--cds-label-01-letter-spacing, 0.32px); + display: block; + padding-top: 0.25rem; +} + +.security--layout-module--list-item__title, .security--layout-module--list-item__description, .security--layout-module--list-item__label { + margin-bottom: 0.25rem; +} + +.security--layout-module--list-item__title:last-child, .security--layout-module--list-item__description:last-child, .security--layout-module--list-item__label:last-child { + margin-bottom: 0; +} + +.security--layout-module--list-item .bx--tag { + margin-top: -0.25rem; + margin-bottom: -0.25rem; +} + +.security--layout-module--list-item__component { + padding-top: 1rem; + margin-bottom: 0; +} + .security--layout-module--title-bar { display: flex; } @@ -27929,22 +28044,6 @@ a.bx--tabs__nav-link:focus, a.bx--tabs__nav-link:active { fill: var(--cds-disabled-02, #525252); } -.bx--tile-group { - box-sizing: border-box; - margin: 0; - padding: 0; - font-size: 100%; - font-family: inherit; - vertical-align: baseline; - border: 0; -} - -.bx--tile-group *, -.bx--tile-group *::before, -.bx--tile-group *::after { - box-sizing: inherit; -} - .bx--time-picker { display: flex; align-items: flex-end; diff --git a/src/components/Breadcrumb/_index.scss b/src/components/Breadcrumb/_index.scss index a30458747..47f22d0a5 100644 --- a/src/components/Breadcrumb/_index.scss +++ b/src/components/Breadcrumb/_index.scss @@ -1,14 +1,13 @@ //// /// Breadcrumb component. /// @group breadcrumb -/// @copyright IBM Security 2019 - 2020 +/// @copyright IBM Security 2019 - 2021 //// @import '@carbon/themes/scss/tokens'; @import '@carbon/type/scss/styles'; @import 'carbon-components/scss/components/breadcrumb/breadcrumb'; -@import 'carbon-components/scss/globals/scss/css--reset'; @import 'carbon-components/scss/globals/scss/vars'; @import '../../globals/namespace/index'; diff --git a/src/components/LayoutModules/ButtonClusterModule/__tests__/ButtonClusterModule.spec.js b/src/components/LayoutModules/ButtonClusterModule/__tests__/ButtonClusterModule.spec.js new file mode 100644 index 000000000..f6e10a884 --- /dev/null +++ b/src/components/LayoutModules/ButtonClusterModule/__tests__/ButtonClusterModule.spec.js @@ -0,0 +1,40 @@ +/** + * @file Button cluster module tests. + * @copyright IBM Security 2020 + */ + +import { render } from '@testing-library/react'; +import React from 'react'; + +import { ButtonClusterModule } from '../../../..'; + +const { name } = ButtonClusterModule; + +describe(name, () => { + test('has no accessibility violations', async () => { + const { container } = render( + {name} + ); + + await expect(container).toHaveNoAxeViolations(); + await expect(container).toHaveNoDAPViolations(name); + }); + + test(`adds content for the '${name}'`, () => { + expect( + render({name}).getByText(name) + ).toBeInTheDocument(); + }); + + test('adds additional props to the containing node', () => { + const dataTestId = 'dataTestId'; + + expect( + render( + + {name} + + ).getByTestId(dataTestId) + ).toBeInTheDocument(); + }); +}); diff --git a/src/components/LayoutModules/ListItemModule/ListItemModule.stories.js b/src/components/LayoutModules/ListItemModule/ListItemModule.stories.js new file mode 100644 index 000000000..80d87621e --- /dev/null +++ b/src/components/LayoutModules/ListItemModule/ListItemModule.stories.js @@ -0,0 +1,264 @@ +/** + * @file List item module stories. + * @copyright IBM Security 2021 + */ + +import { + Bee16, + InProgress16, + Locked16, + UserAvatar20, +} from '@carbon/icons-react'; + +import { action } from '@storybook/addon-actions'; + +import React from 'react'; + +import { getDocsParameters } from '../../../../.storybook'; +import withResponsive from '../../../../.storybook/decorators'; + +import { ICA, ListItemModule, ProfileImage, StatusIcon, Tag } from '../../..'; + +import getTitle from '../stories'; +import page from './index.mdx'; + +export default { + title: getTitle(ListItemModule.name), + component: ListItemModule, + parameters: { + docs: { page }, + + ...getDocsParameters(), + }, + decorators: [withResponsive], +}; + +export const Default = () => ( + + {({ Column, getLayoutProps }) => { + const { + avatar, + component, + description, + icon, + label, + title, + } = getLayoutProps(); + + return ( + <> + + + + + + {console.log()} +

List title

+ +

+ Description here. It can go up to three lines before truncating. +

+ +
Nested Component
+ + Time stamp / label + + + + +
+ + + + + + ); + }} +
+); + +export const NonInteractive = () => ( + + {({ Column, getLayoutProps }) => { + const { label, title } = getLayoutProps(); + + return ( + +

List title

+ + Label +
+ ); + }} +
+); + +export const AsButton = () => ( + + {({ Column, getLayoutProps }) => { + const { label, title } = getLayoutProps(); + + return ( + +

List title

+ + Label +
+ ); + }} +
+); + +export const WithProfileImage = () => ( + + {({ Column, getLayoutProps }) => { + const { label, description, profileimage } = getLayoutProps(); + + return ( + <> + + Profile Image, + image_url: null, + name: { + first_name: 'Sample', + surname: 'User', + }, + }} + /> + + + +

+ Description here. It can go up to three lines before truncating. +

+ + Today 11:50 AM +
+ + ); + }} +
+); + +export const WithTag = () => ( + + {({ Column, getLayoutProps }) => { + const { title, farsidecolumn } = getLayoutProps(); + + return ( + <> + +

Bill Callahan

+
+ + + 16 + + + ); + }} +
+); + +export const WithComponent = () => ( + + {({ Column, getLayoutProps }) => { + const { component, description, icon, title } = getLayoutProps(); + + return ( + <> + + + +

List title

+ +

+ Description here. It can go up to three lines before truncating. +

+ +
+ +
+
+ + ); + }} +
+); + +export const WithStatusIcon = () => ( + + {({ Column, getLayoutProps }) => { + const { farsidecolumn, label, title } = getLayoutProps(); + + return ( + <> + +

GPON vulnerability exploited

+ + Threat activity +
+ + + + + + ); + }} +
+); + +export const WithInProgressIcon = () => ( + + {({ Column, getLayoutProps }) => { + const { avatar, icon, title } = getLayoutProps(); + + return ( + <> + + + + + +

Create new doc types

+ + + + +
+ + ); + }} +
+); + +NonInteractive.parameters = { + viewMode: 'canvas', +}; + +AsButton.parameters = { + viewMode: 'canvas', +}; + +WithProfileImage.parameters = { + viewMode: 'canvas', +}; + +WithTag.parameters = { + viewMode: 'canvas', +}; + +WithComponent.parameters = { + viewMode: 'canvas', +}; + +WithStatusIcon.parameters = { + viewMode: 'canvas', +}; + +WithInProgressIcon.parameters = { + viewMode: 'canvas', +}; diff --git a/src/components/LayoutModules/ListItemModule/_index.scss b/src/components/LayoutModules/ListItemModule/_index.scss new file mode 100644 index 000000000..b08985c4d --- /dev/null +++ b/src/components/LayoutModules/ListItemModule/_index.scss @@ -0,0 +1,130 @@ +//// +/// List item module. +/// @group list-item-module +/// @copyright IBM Security 2021 +//// + +@import '@carbon/layout/scss/spacing'; +@import '@carbon/themes/scss/tokens'; +@import '@carbon/type/scss/styles'; + +@import '../../../globals/border/index'; + +@import 'carbon-components/scss/globals/scss/helper-mixins'; + +@import '../../Component/mixins'; +@import '../variables'; + +@import '../../../globals/motion/index'; + +@include security--component($name: #{$layout-modules__name}--list-item) { + /// Spacing. + /// @type Length + $list-item-module__spacing: $carbon--spacing-02; + $list-item-module__border: $border__sizing__width solid $ui-03; + + display: flex; + padding: $carbon--spacing-05; + background-color: $ui-01; + border: none; + border-top: $list-item-module__border; + + &:last-of-type { + border-bottom: $list-item-module__border; + } + + &--a, + &--button { + @include transition($transition-property: background-color); + + color: inherit; + text-decoration: none; + cursor: pointer; + } + + &--button { + font-family: inherit; + text-align: left; + width: 100%; + } + + &--a:focus, + &--a:hover, + &--button:focus, + &--button:hover { + background-color: $hover-ui; + } + + &--a:focus, + &--button:focus { + @include focus-outline($type: outline); + } + + &__farside-column { + margin-left: auto; + } + + &__title, + &__description { + margin-top: 0; + } + + &__icon { + margin-right: $carbon--spacing-05; + margin-bottom: $carbon--spacing-05; + } + + &__avatar { + margin-top: $list-item-module__spacing; + margin-right: $carbon--spacing-04; + + &:first-child { + margin-top: 0; + } + } + + &__profile-image { + margin-right: $carbon--spacing-04; + height: 24px; + width: 24px; + } + + &__title { + @include carbon--type-style($name: body-short-01); + } + + &__description, + &__label { + color: $text-02; + } + + &__description { + @include carbon--type-style($name: body-short-01); + } + + &__label { + @include carbon--type-style($name: label-01); + display: block; + padding-top: $list-item-module__spacing; + } + + &__title, + &__description, + &__label { + margin-bottom: $list-item-module__spacing; + + &:last-child { + margin-bottom: 0; + } + } + + .bx--tag { + margin-top: -$carbon--spacing-02; + margin-bottom: -$carbon--spacing-02; + } + + &__component { + padding-top: $carbon--spacing-05; + margin-bottom: 0; + } +} diff --git a/src/components/LayoutModules/ListItemModule/images/anatomy.png b/src/components/LayoutModules/ListItemModule/images/anatomy.png new file mode 100644 index 000000000..b975dff07 Binary files /dev/null and b/src/components/LayoutModules/ListItemModule/images/anatomy.png differ diff --git a/src/components/LayoutModules/ListItemModule/images/do.png b/src/components/LayoutModules/ListItemModule/images/do.png new file mode 100644 index 000000000..c317fd483 Binary files /dev/null and b/src/components/LayoutModules/ListItemModule/images/do.png differ diff --git a/src/components/LayoutModules/ListItemModule/images/dont.png b/src/components/LayoutModules/ListItemModule/images/dont.png new file mode 100644 index 000000000..aa1a11f0c Binary files /dev/null and b/src/components/LayoutModules/ListItemModule/images/dont.png differ diff --git a/src/components/LayoutModules/ListItemModule/images/variants.png b/src/components/LayoutModules/ListItemModule/images/variants.png new file mode 100644 index 000000000..82091c3c9 Binary files /dev/null and b/src/components/LayoutModules/ListItemModule/images/variants.png differ diff --git a/src/components/LayoutModules/ListItemModule/index.js b/src/components/LayoutModules/ListItemModule/index.js new file mode 100644 index 000000000..d0d76d447 --- /dev/null +++ b/src/components/LayoutModules/ListItemModule/index.js @@ -0,0 +1,92 @@ +/** + * @file List item module. + * @copyright IBM Security 2021 + */ + +import classnames from 'classnames'; +import { elementType, func, string } from 'prop-types'; +import React from 'react'; + +import LayoutModule, { layoutModuleNamespace } from '../LayoutModule'; + +const namespace = 'list-item'; + +/** + * The ListItem is a module designed for flexibility. It consists of optional pieces that can be used to design & build variants with universal consistency. + */ +const ListItemModule = ({ children, as, href, onClick, ...other }) => { + const content = children({ + Column: props => ( + + ), + + getLayoutProps: ({ className, ...rest } = {}) => { + const assignClassName = type => ({ + className: classnames(className, { + [`${layoutModuleNamespace}--${namespace}__${type}`]: type, + }), + }); + + return { + avatar: assignClassName('avatar'), + component: assignClassName('component'), + description: assignClassName('description'), + farsidecolumn: assignClassName('farside-column'), + icon: assignClassName('icon'), + label: assignClassName('label'), + profileimage: assignClassName('profile-image'), + title: assignClassName('title'), + ...rest, + }; + }, + }); + + let component = 'div'; + + if (as !== 'div') { + component = as; + } else if (href) { + component = 'a'; + } else if (onClick) { + component = 'button'; + } + + return ( + + {content} + + ); +}; + +ListItemModule.propTypes = { + /** Provide the content for the `ListItemModule` */ + children: func.isRequired, + + /** Provide a custom element to be rendered instead of the default */ + as: elementType, + + /** + * Optionally specify an href for the ListItemModule to become an `` element + */ + href: string, + + /** + * Provide an optional function to be called when the ListItemModule + * is clicked, turning the ListItemModule into a `