Skip to content

Commit

Permalink
Merge pull request #1147 from securityscorecard/next
Browse files Browse the repository at this point in the history
🚀 Next release
  • Loading branch information
ajkl2533 authored Oct 3, 2024
2 parents f7850ac + db9361a commit f0f668c
Show file tree
Hide file tree
Showing 302 changed files with 1,245 additions and 793 deletions.
8 changes: 4 additions & 4 deletions .storybook/blocks/FontPallete.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { FunctionComponent } from 'react';
import { ReactNode } from 'react';
import styled from 'styled-components';

import { theme } from '../../src/theme';
Expand Down Expand Up @@ -73,15 +73,15 @@ interface FontProps {
sampleText: string;
}

export const FontItem: FunctionComponent<FontProps> = ({
export const FontItem = ({
title,
subtitle,
fontFamily = theme.typography.family.base,
fontWeight = theme.typography.weight.regular,
fontSize = theme.typography.size.lg,
lineHeight = 'normal',
sampleText,
}) => {
}:FontProps) => {
return (
<Item>
<ItemDescription>
Expand All @@ -100,7 +100,7 @@ export const FontItem: FunctionComponent<FontProps> = ({
);
};

export const FontPalette: FunctionComponent = ({ children, ...props }) => (
export const FontPalette = ({ children, ...props }: {children: ReactNode}) => (
<List {...props}>
<ListHeading>
<ListName>Name</ListName>
Expand Down
1 change: 0 additions & 1 deletion .storybook/blocks/SpaceScale.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import { endsWith } from 'ramda'

import { Table, TableBody, TableHead, Token } from "./components"
Expand Down
6 changes: 0 additions & 6 deletions .storybook/decorators/withThemeByClassName.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,12 @@ import type { StoryContext } from 'storybook/internal/types';
const PARAM_KEY = 'themes' as const;
const ADDON_ID = `storybook/${PARAM_KEY}` as const;
const GLOBAL_KEY = 'theme' as const;
const THEME_SWITCHER_ID = `${ADDON_ID}/theme-switcher` as const;

interface ThemeAddonState {
themesList: string[];
themeDefault?: string;
}

const DEFAULT_ADDON_STATE: ThemeAddonState = {
themesList: [],
themeDefault: undefined,
};

interface ThemeParameters {
themeOverride?: string;
disable?: boolean;
Expand Down
Binary file modified .storybook/image-snapshots/expected/Iconography_List_List.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 13 additions & 2 deletions .storybook/preview.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import React, { useEffect } from 'react';
import { useEffect } from 'react';
import type { Preview } from '@storybook/react';
import { withScreenshot } from 'storycap';
import i18n from 'i18next';
import { initReactI18next, I18nextProvider } from 'react-i18next';
import ICU from 'i18next-icu';
import icuEn from 'i18next-icu/locale-data/en';
import icuJa from 'i18next-icu/locale-data/ja';
import icuEs from 'i18next-icu/locale-data/es';
import icuPt from 'i18next-icu/locale-data/pt';
import icuCs from 'i18next-icu/locale-data/cs';

import { DSProvider, createIconLibrary } from '../src/theme';
import en from '../src/locales/en-US';
Expand All @@ -18,6 +24,7 @@ import '@fontsource/inter/600.css';
import '@fontsource/inter/700.css';
import '@fontsource/space-mono/400.css';
import '../src/tokens/tokens.css';
import { SlowBuffer } from 'buffer';

function clearDatatableLS() {
Object.keys(localStorage)
Expand All @@ -28,8 +35,12 @@ clearDatatableLS();
createIconLibrary();
window.Math.random = () => 0.5;

i18n.use(initReactI18next).init({
i18n.use(ICU).use(initReactI18next).init({
debug: true,
i18nFormat: {
localeData: [icuEn, icuJa, icuEs, icuPt, icuCs],
formats: {},
},
resources: {
'en-US': { sscds: en },
'ja-JP': { sscds: ja },
Expand Down
62 changes: 62 additions & 0 deletions docs/localization.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,68 @@ We've chosen to implement localization using `i18next` and `react-i18next`. This

`i18next` provides a robust framework for managing translations, while `react-i18next` offers React-specific bindings that make it simple to use localized content within our components. This approach allows for dynamic language switching and efficient management of translation resources.

## String interpolation

Our localization system utilizes the ICU (International Components for Unicode) Message Format instead of the default i18next syntax for string interpolation and pluralization. This format provides a more powerful and standardized way to handle complex localization scenarios.

### What is ICU Message Format?

ICU Message Format is a standardized localization format used widely in software internationalization. It allows for sophisticated string formatting, including pluralization, gender, and selectional mechanisms. This format is more expressive than simple key-value pairs and can handle a wide variety of linguistic rules across different languages.

### How It Works

ICU Message Format uses placeholders and formatting instructions within curly braces {}. These placeholders can be simple variables, or they can include formatting options for numbers, dates, and pluralization.

### Syntax Examples

#### Basic String Interpolation

To insert a variable into a string, use the variable name within curly braces:

```
"Hello, {name}!"
```

When providing the translation, you would use:

```json
{
"greeting": "Hello, {name}!"
}
```

And in your code:

```js
t('greeting', { name: 'Alice' });
```

This would result in: "Hello, Alice!"

#### Pluralization

For pluralization, use the plural keyword followed by the variable and the different forms:

```
"{count, plural, =0 {No items} one {{count} item} other {{count} items}}"
```

In your translation file:

```json
{
"itemCount": "{count, plural, =0 {No items} one {{count} item} other {{count} items}}"
}
```

In your code:

```js
t('itemCount', { count: 0 }); // "No items"
t('itemCount', { count: 1 }); // "1 item"
t('itemCount', { count: 5 }); // "5 items"
```

## Integration into Existing Apps

Integrating our localized components into your existing application is straightforward. Here are the steps:
Expand Down
40 changes: 27 additions & 13 deletions jest.setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,37 @@ import '@testing-library/jest-dom/extend-expect';
import 'jest-styled-components';
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import ICU from 'i18next-icu';
import icuEn from 'i18next-icu/locale-data/en';
import icuJa from 'i18next-icu/locale-data/ja';
import icuEs from 'i18next-icu/locale-data/es';
import icuPt from 'i18next-icu/locale-data/pt';
import icuCs from 'i18next-icu/locale-data/cs';

import { createIconLibrary, resetIconLibrary } from './src';
import en from './src/locales/en-US';

i18n.use(initReactI18next).init({
resources: {
'en-US': { sscds: en },
},
defaultNS: 'sscds',
keySeparator: false,
nsSeparator: '|',
lng: 'en-US',
fallbackLng: 'en-US',
interpolation: {
escapeValue: false,
},
});
i18n
.use(ICU)
.use(initReactI18next)
.init({
debug: true,
i18nFormat: {
localeData: [icuEn, icuJa, icuEs, icuPt, icuCs],
formats: {},
},
resources: {
'en-US': { sscds: en },
},
defaultNS: 'sscds',
keySeparator: false,
nsSeparator: '|',
lng: 'en-US',
fallbackLng: 'en-US',
interpolation: {
escapeValue: false,
},
});

Object.defineProperty(document, 'fonts', {
value: {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
"dedent": "^1.5.1",
"fast-deep-equal": "^3.1.3",
"i18next": ">=17.3.1",
"i18next-icu": "^1.4.2",
"numeral": "^2.0.6",
"pullstate": "^1.23.0",
"ramda": "^0.28.0",
Expand Down
6 changes: 3 additions & 3 deletions src/components/Banner/Banner.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useMemo } from 'react';
import { type MouseEvent, useMemo } from 'react';
import styled from 'styled-components';
import { isNonEmptyArray, noop } from 'ramda-adjunct';
import cls from 'classnames';
Expand Down Expand Up @@ -50,10 +50,10 @@ const BannerContent = ({ children, actions, isInline }: BannerContentProps) => (
{actions.map((action) => (
<Button
key={action.name}
href={(action as AbsoluteLinkActionKind<[React.MouseEvent]>).href}
href={(action as AbsoluteLinkActionKind<[MouseEvent]>).href}
name={action.name}
style={{ height: '2rem' }}
to={(action as RelativeLinkActionKind<[React.MouseEvent]>).to}
to={(action as RelativeLinkActionKind<[MouseEvent]>).to}
variant="outline"
onClick={action.onClick}
>
Expand Down
8 changes: 4 additions & 4 deletions src/components/Banner/Banner.types.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React, { ReactNode } from 'react';
import { type MouseEvent, type MouseEventHandler, ReactNode } from 'react';

import { BaseToastBannerProps } from '../_internal/BaseToastBanner/BaseToastBanner.types';
import { ActionKinds } from '../../types/action.types';
import { BannerVariants } from './Banner.enums';

export type ActionsArray = readonly [
ActionKinds<[React.MouseEvent]>?,
ActionKinds<[React.MouseEvent]>?,
ActionKinds<[MouseEvent]>?,
ActionKinds<[MouseEvent]>?,
];

type Variants = (typeof BannerVariants)[keyof typeof BannerVariants];
Expand All @@ -15,7 +15,7 @@ export type BannerProps = {
/** Toggles display of the close button */
isDismissable?: boolean;
/** Callback triggered on close button click */
onClose?: React.MouseEventHandler;
onClose?: MouseEventHandler;
/** Banner container width in which action buttons will switch the layout from inline to block */
changeLayoutBreakpoint?: number;
className?: string;
Expand Down
1 change: 0 additions & 1 deletion src/components/Breadcrumbs/Breadcrumbs.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import { Meta, StoryFn } from '@storybook/react';
import { MemoryRouter } from 'react-router-dom';

Expand Down
1 change: 0 additions & 1 deletion src/components/Breadcrumbs/Breadcrumbs.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { fireEvent, screen, waitFor } from '@testing-library/react';
import React from 'react';

import { renderWithProviders } from '../../utils/tests/renderWithProviders';
import BreadcrumbItem from './BreadcrumbItem';
Expand Down
40 changes: 22 additions & 18 deletions src/components/Breadcrumbs/Breadcrumbs.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import * as React from 'react';
import { slice } from 'ramda';
import styled from 'styled-components';
import { isNilOrEmpty, isNotNilOrEmpty } from 'ramda-adjunct';
import cls from 'classnames';
import {
Children,
type MouseEvent,
type ReactElement,
type ReactNode,
cloneElement,
isValidElement,
} from 'react';

import type {
BreadcrumbItemProps,
Expand Down Expand Up @@ -31,7 +38,7 @@ const itemsAfterCollapse = 3;
const itemsBeforeCollapse = 1;

// Build list of breadcrumbs interspersing a separator
const insertSeparators = (items: React.ReactElement[]) => {
const insertSeparators = (items: ReactElement[]) => {
return items.reduce((prev, current, index) => {
if (index < items.length - 1) {
return [
Expand All @@ -52,7 +59,7 @@ const insertSeparators = (items: React.ReactElement[]) => {
}, []);
};

const renderDropdown = (actions: ActionKinds<React.MouseEvent[]>[]) => (
const renderDropdown = (actions: ActionKinds<MouseEvent[]>[]) => (
<li key="breadcrumbs-dropdown">
<DropdownMenu
actions={actions}
Expand All @@ -69,8 +76,8 @@ const renderDropdown = (actions: ActionKinds<React.MouseEvent[]>[]) => (

// this renders the list of items only when the count of the actions is bigger than 2
const renderItemsBeforeAndAfter = (
allItems: React.ReactNode[],
allDropdownActions: ActionKinds<React.MouseEvent[]>[],
allItems: ReactNode[],
allDropdownActions: ActionKinds<MouseEvent[]>[],
) => {
const dropdown = renderDropdown(allDropdownActions);
return [
Expand All @@ -81,28 +88,25 @@ const renderItemsBeforeAndAfter = (
};

const Breadcrumbs = ({ children, className, ...props }: BreadcrumbsProps) => {
const allItems = React.Children.map(children, (breadcrumbItem) => {
if (!React.isValidElement(breadcrumbItem)) {
const allItems = Children.map(children, (breadcrumbItem) => {
if (!isValidElement(breadcrumbItem)) {
return null;
}

return React.cloneElement(
breadcrumbItem as React.ReactElement<BreadcrumbItemProps>,
{
isSelected:
isNilOrEmpty(breadcrumbItem.props.to) &&
isNilOrEmpty(breadcrumbItem.props.href),
...props,
},
);
return cloneElement(breadcrumbItem as ReactElement<BreadcrumbItemProps>, {
isSelected:
isNilOrEmpty(breadcrumbItem.props.to) &&
isNilOrEmpty(breadcrumbItem.props.href),
...props,
});
});

const allDropdownActions = slice(
itemsBeforeCollapse,
-Math.abs(itemsAfterCollapse),
)(
React.Children.toArray(children).map((breadcrumbItem) => {
if (!React.isValidElement(breadcrumbItem)) {
Children.toArray(children).map((breadcrumbItem) => {
if (!isValidElement(breadcrumbItem)) {
return null;
}
return {
Expand Down
1 change: 0 additions & 1 deletion src/components/Button/Button.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import { Meta, StoryFn } from '@storybook/react';

import { SSCIconNames } from '../../theme/icons/icons.enums';
Expand Down
4 changes: 2 additions & 2 deletions src/components/Card/Card.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import { forwardRef } from 'react';
import styled, { css } from 'styled-components';
import cls from 'classnames';

Expand Down Expand Up @@ -46,7 +46,7 @@ export const CardContainer = styled.div<{
`var(--sscds-space-${$verticalPadding}) var(--sscds-space-${$horizontalPadding})`};
`;

const Card = React.forwardRef<HTMLDivElement, CardProps>(
const Card = forwardRef<HTMLDivElement, CardProps>(
(
{ children, shouldAlignLastItemToBottom = false, as, ...props }: CardProps,
ref,
Expand Down
Loading

0 comments on commit f0f668c

Please sign in to comment.