Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release 1.3.1 #196

Merged
merged 6 commits into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ dist/
.idea/
settings.json
.vscode
site/
site/
coverage/
5 changes: 4 additions & 1 deletion .storybook/main.mts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ import { dirname } from 'path';

export default {
stories: ['../packages/**/*.stories.[tj]sx'],
addons: ['@storybook/addon-actions'],
addons: ['@storybook/addon-essentials', '@storybook/addon-actions', '@storybook/addon-interactions'],
framework: {
name: '@storybook/react-vite',
options: {}
},
features: {
interactionsDebugger: true,
},
async viteFinal(config) {
return mergeConfig(config, {
root: dirname(require.resolve('@storybook/builder-vite')),
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## [1.3.1-next.1](https://github.com/warp-ds/react/compare/v1.3.0...v1.3.1-next.1) (2024-01-24)


### Bug Fixes

* SSR compatibility for textfield ([#195](https://github.com/warp-ds/react/issues/195)) ([7779237](https://github.com/warp-ds/react/commit/77792373fb32d5ec89005a3cafa0ef1eb78aa122))

# [1.3.0](https://github.com/warp-ds/react/compare/v1.2.2...v1.3.0) (2024-01-17)


Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,13 @@ pnpm add @warp-ds/react
```html
<script src="https://assets.finn.no/pkg/@warp-ds/react/v1"></script>
```
### Testing

This app contains 2 types of tests:

1. Unit tests: These are based on vitest and testing-library. Run these tests in watch mode using `pnpm test:watch` or with coverage report by `pnpm test:unit`.
2. Component tests: These are currently written with storybook. You can start storybook using `pnpm dev` and see the tests in the interavtive panel for a story. Alternatively, you can run these tests on CI/Command line using `pnpm test:storybook`. This will start storybook and run tests concurrently.
Note: you might have to run `pnpm exec playwright install` first time as you might not have the chromium playwright executable available locally.

### Documentation

Expand Down
46 changes: 32 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@warp-ds/react",
"version": "1.3.0",
"version": "1.3.1-next.1",
"type": "module",
"repository": "[email protected]:warp-ds/react.git",
"license": "Apache-2.0",
Expand All @@ -22,25 +22,30 @@
"access": "public"
},
"peerDependencies": {
"react": "17.0.2"
"react": "18.2.0"
},
"scripts": {
"commit": "cz",
"build-storybook": "storybook build && touch ./storybook-static/.nojekyll",
"build:clean": "rm -rf dist",
"build:eik": "node esbuild.mjs 17 && node esbuild.mjs 18",
"build:types": "tsc && sed 's+declare module \"index\"+declare module \"@warp-ds/react\"+g' ./dist/npm/index.d.ts > ./dist/npm/updated.d.ts && mv ./dist/npm/updated.d.ts ./dist/npm/index.d.ts",
"build:npm": "npx esbuild ./packages/index.ts --outdir=dist/npm --target=es2017 --bundle --sourcemap --format=esm --external:react",
"build:node": "npx esbuild ./packages/index.ts --outdir=dist/npm --out-extension:.js=.cjs --target=es2017 --bundle --sourcemap --format=cjs --external:react",
"build:npm": "npx esbuild ./packages/index.ts --outdir=dist/npm --target=es2017 --bundle --sourcemap --format=esm --external:react",
"build:types": "tsc && sed 's+declare module \"index\"+declare module \"@warp-ds/react\"+g' ./dist/npm/index.d.ts > ./dist/npm/updated.d.ts && mv ./dist/npm/updated.d.ts ./dist/npm/index.d.ts",
"build": "pnpm run build:clean && pnpm run build:npm && pnpm run build:node && pnpm run build:eik && pnpm run build:types",
"commit": "cz",
"dev": "storybook dev -p 9003 --ci",
"build-storybook": "storybook build",
"format": "prettier --write . --ignore-path .gitignore",
"lint": "pnpm run lint:format && pnpm run lint:eslint",
"lint:eslint": "eslint . --ext ts,tsx,js,jsx,cjs --max-warnings 0 --ignore-path .gitignore",
"lint:format": "prettier --check . --ignore-path .gitignore",
"semantic-release": "semantic-release",
"lint": "pnpm run lint:format && pnpm run lint:eslint",
"messages:compile": "lingui compile",
"messages:extract": "lingui extract",
"messages:compile": "lingui compile"
"semantic-release": "semantic-release",
"test-storybook": "test-storybook --url http://localhost:9003",
"test:storybook": "concurrently -k -s first \"pnpm run dev\" \"wait-on http://localhost:9003 && pnpm run test-storybook\"",
"test:watch": "vitest run --watch",
"test:unit": "vitest run --coverage",
"test": "pnpm test:unit && pnpm test:storybook"
},
"devDependencies": {
"@babel/core": "^7.23.7",
Expand All @@ -55,15 +60,23 @@
"@semantic-release/changelog": "^6.0.1",
"@semantic-release/git": "^10.0.1",
"@storybook/addon-actions": "^7.6.9",
"@storybook/addon-essentials": "^7.6.10",
"@storybook/addon-interactions": "^7.6.9",
"@storybook/builder-vite": "^7.6.9",
"@storybook/cli": "^7.6.9",
"@storybook/react-vite": "^7.6.9",
"@storybook/test": "^7.6.9",
"@storybook/test-runner": "^0.16.0",
"@testing-library/jest-dom": "^6.2.0",
"@testing-library/react": "^14.1.2",
"@types/node": "^20.11.5",
"@types/react": "^17.0.75",
"@types/react-dom": "^17.0.25",
"@types/react": "^18.2.48",
"@types/react-dom": "^18.2.18",
"@typescript-eslint/eslint-plugin": "^6.19.0",
"@typescript-eslint/parser": "^6.19.0",
"@vitejs/plugin-react": "^4.2.1",
"@vitest/coverage-v8": "^1.2.1",
"concurrently": "^8.2.2",
"cz-conventional-changelog": "^3.3.0",
"element-collapse": "^1.0.1",
"esbuild": "0.19.11",
Expand All @@ -75,17 +88,22 @@
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-storybook": "^0.6.15",
"happy-dom": "^13.1.4",
"jest": "^29.7.0",
"playwright": "^1.41.0",
"prettier": "^3.2.4",
"react": "16.14.0",
"react-dom": "16.14.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-focus-lock": "^2.5.2",
"resize-observer-polyfill": "^1.5.1",
"semantic-release": "^23.0.0",
"semantic-release-slack-bot": "^4.0.0",
"storybook": "^7.6.9",
"typescript": "5.3.3",
"unocss": "^0.58.0",
"vite": "^5.0.0"
"vite": "^5.0.0",
"vitest": "^1.2.1",
"wait-on": "^7.2.0"
},
"dependencies": {
"@chbphone55/classnames": "^2.0.0",
Expand Down
50 changes: 44 additions & 6 deletions packages/alert/stories/Alert.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,39 @@ import React from 'react';
import { Button } from '../../button/src';
import { Alert, AlertProps } from '../src';

const metadata = { title: 'FeedbackIndicators/Alert' };
export default metadata;
import { within, expect } from '@storybook/test';

export default { title: 'FeedbackIndicators/Alert', component: Alert };

const Template = (args) => <Alert {...args} />;
export const Default = Template.bind({});
Default.args = {
type: 'negative',
show: true,
children: 'This is "negative" variant of the alert component',
};

export const Variants = () => (
<div className="flex flex-col gap-y-16">
<div>
<div data-testid="negative">
<h3>Negative</h3>
<Alert type="negative" show>
This is "negative" variant of the alert component
</Alert>
</div>
<div>
<div data-testid="positive">
<h3>Positive</h3>
<Alert type="positive" show role="status">
This is "positive" variant of the alert component
</Alert>
</div>
<div>
<div data-testid="warning">
<h3>Warning</h3>
<Alert type="warning" show>
This is "warning" variant of the alert component
</Alert>
</div>
<div>
<div data-testid="info">
<h3>Info</h3>
<Alert type="info" show role="status">
This is "info" variant of the alert component
Expand All @@ -34,6 +43,28 @@ export const Variants = () => (
</div>
);

Variants.play = async ({ canvasElement }) => {
const canvas = within(canvasElement);

const negative = within(canvas.getByTestId('negative'));
// test the outer container
await expect(negative.getByRole('alert')).toBeInTheDocument();
// test the icon
await expect(negative.getByTitle(/Red octagon/)).toBeInTheDocument();

const positive = within(canvas.getByTestId('positive'));
await expect(positive.getByRole('status')).toBeInTheDocument();
await expect(positive.getByTitle(/Green circle/)).toBeInTheDocument();

const warning = within(canvas.getByTestId('warning'));
await expect(warning.getByRole('alert')).toBeInTheDocument();
await expect(warning.getByTitle(/Yellow warning/)).toBeInTheDocument();

const info = within(canvas.getByTestId('info'));
await expect(info.getByRole('status')).toBeInTheDocument();
await expect(info.getByTitle(/Blue circle/)).toBeInTheDocument();
};

const InteractiveContent = ({type}: Pick<AlertProps, "type">) => (
<>
<h4 className="h5">This is {type} variant of the alert component</h4>
Expand Down Expand Up @@ -124,3 +155,10 @@ export const WithOverriddenRole = () => {
</>
);
};

export const NegativeAlertTask = Template.bind({});
NegativeAlertTask.args = { ...Default.args };
NegativeAlertTask.play = async ({ canvasElement }) => {
const canvas = within(canvasElement);
await expect(canvas.getByRole('alert')).toBeInTheDocument();
};
3 changes: 1 addition & 2 deletions packages/textfield/src/component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,8 @@ export const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
} = props;

activateI18n(enMessages, nbMessages, fiMessages);

const id = useId(providedId);

const helpId = helpText ? `${id}__hint` : undefined;
const isInvalid = invalid || error;

Expand Down
18 changes: 13 additions & 5 deletions packages/textfield/stories/Textfield.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
import React from 'react';
import { action } from '@storybook/addon-actions';
import { TextField as TroikaTextField } from '../src';
import { TextField as WarpTextField } from '../src';
import { Affix } from '../../_helpers';

const metadata = { title: 'Forms/TextField' };
export default metadata;
export default { title: 'Forms/TextField', component: WarpTextField };

const Template = (args) => <WarpTextField label="Address"
onChange={action('change')}
onFocus={action('focus')}
onBlur={action('blur')} {...args} />;
export const Default = Template.bind({});
Default.args = {
value: 'test',
};

const TextField = (args) => (
<TroikaTextField
<WarpTextField
label="Address"
onChange={action('change')}
onFocus={action('focus')}
Expand Down Expand Up @@ -54,7 +62,7 @@ export const longLabelPrefix = () => (
<TextField className="[--w-prefix-width:90px]" value="With some value">
<Affix prefix label="Long prefix" />
</TextField>
);
);

export const clearSuffix = () => (
<TextField>
Expand Down
96 changes: 4 additions & 92 deletions packages/utils/src/useId.ts
Original file line number Diff line number Diff line change
@@ -1,97 +1,9 @@
/*
* Let's see if we can make sense of why this hook exists and its
* implementation.
*
* Some background:
* 1. Accessibiliy APIs rely heavily on element IDs
* 2. Requiring developers to put IDs on every element in Reach UI is both
* cumbersome and error-prone
* 3. With a component model, we can generate IDs for them!
*
* Solution 1: Generate random IDs.
*
* This works great as long as you don't server render your app. When React (in
* the client) tries to reuse the markup from the server, the IDs won't match
* and React will then recreate the entire DOM tree.
*
* Solution 2: Increment an integer
*
* This sounds great. Since we're rendering the exact same tree on the server
* and client, we can increment a counter and get a deterministic result between
* client and server. Also, JS integers can go up to nine-quadrillion. I'm
* pretty sure the tab will be closed before an app never needs
* 10 quadrillion IDs!
*
* Problem solved, right?
*
* Ah, but there's a catch! React's concurrent rendering makes this approach
* non-deterministic. While the client and server will end up with the same
* elements in the end, depending on suspense boundaries (and possibly some user
* input during the initial render) the incrementing integers won't always match
* up.
*
* Solution 3: Don't use IDs at all on the server; patch after first render.
*
* What we've done here is solution 2 with some tricks. With this approach, the
* ID returned is an empty string on the first render. This way the server and
* client have the same markup no matter how wild the concurrent rendering may
* have gotten.
*
* After the render, we patch up the components with an incremented ID. It
* doesn't have to be incremented, though; we could do something random, but
* incrementing a number is probably the cheapest thing we can do.
*
* @TODO Note that this should be axed if useOpaqueIdentifier becomes stable
* https://github.com/facebook/react/pull/17322
*/

import { useState, useEffect } from 'react';
import { useLayoutEffect } from './useIsomorphicLayoutEffect.js';

let serverHandoffComplete = false;
// Generate a pseudorandom seed to prefix to each generated id instead of solely relying on the counter.
// We don't want id collisions across React roots/podlets.
const prefix = generateId();

let id = 0;
const genId = () => {
id = id + 1;
return prefix + id;
};
import { useId as reactUseId } from 'react';

export const useId = (hasFallback?): string => {
/*
* If this instance isn't part of the initial render, we don't have to do the
* double render/patch-up dance. We can just generate the ID and return it.
*/
const initialId = hasFallback || (serverHandoffComplete ? genId() : null);

const [id, setId] = useState(initialId);

useLayoutEffect(() => {
if (id === null) {
/*
* Patch the ID after render. We do this in `useLayoutEffect` to avoid any
* rendering flicker, though it'll make the first render slower (unlikely
* to matter, but you're welcome to measure your app and let us know if
* it's a problem).
*/
setId(genId());
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

useEffect(() => {
if (serverHandoffComplete === false) {
/*
* Flag all future uses of `useId` to skip the update dance. This is in
* `useEffect` because it goes after `useLayoutEffect`, ensuring we don't
* accidentally bail out of the patch-up dance prematurely.
*/
serverHandoffComplete = true;
}
}, []);
return id;
// reactUseId returns a string that includes colons (:), e.g., :r0:, :r1:, etc.
// This string is NOT supported in CSS selectors. Hence the replace.
return hasFallback ?? reactUseId().replace(/:/g, '');
};

/**
Expand Down
Loading
Loading