mirror of
https://github.com/navikt/aksel.git
synced 2026-10-13 01:02:02 +00:00
Compare commits
90
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9102781d5c | ||
|
|
4405c89d5f | ||
|
|
309cd0fba3 | ||
|
|
5f1ca61501 | ||
|
|
a75622752e | ||
|
|
62a00f745c | ||
|
|
7b946d76ac | ||
|
|
0e42521c90 | ||
|
|
95e5891b27 | ||
|
|
9230adaa89 | ||
|
|
1885f0e935 | ||
|
|
c718348749 | ||
|
|
2166a7933f | ||
|
|
70ba56e218 | ||
|
|
69a21190b0 | ||
|
|
72c52f6cda | ||
|
|
ae297ee3fd | ||
|
|
e3ce3d0a28 | ||
|
|
d4cf0d2b1e | ||
|
|
60f1397043 | ||
|
|
4d78c35ec2 | ||
|
|
343fd910a8 | ||
|
|
86c3545fb7 | ||
|
|
f1341c6666 | ||
|
|
0949ba0b56 | ||
|
|
2ae9f382fb | ||
|
|
95476ff04e | ||
|
|
12be3f9ba6 | ||
|
|
9d00cecc92 | ||
|
|
755fd0cf3c | ||
|
|
9295bda19d | ||
|
|
2688b488e5 | ||
|
|
6aacf70c3d | ||
|
|
d211a06f5f | ||
|
|
e8ea9a8e34 | ||
|
|
7fef4d5ef8 | ||
|
|
a7f04b4902 | ||
|
|
e0fa04930d | ||
|
|
57cea8cb6d | ||
|
|
1884c6345b | ||
|
|
cb962e0332 | ||
|
|
b246578fe9 | ||
|
|
1d42e573bf | ||
|
|
1eea43c36f | ||
|
|
6fe7227345 | ||
|
|
362c8991c9 | ||
|
|
204bc1e010 | ||
|
|
7ac379364e | ||
|
|
cff48a3c47 | ||
|
|
a8c4aa785f | ||
|
|
aebbaef65e | ||
|
|
5009beddb0 | ||
|
|
7247c19b15 | ||
|
|
8e8d8a093f | ||
|
|
08bd97cfa1 | ||
|
|
cd80f303c8 | ||
|
|
23a32607e7 | ||
|
|
15a7ae44d4 | ||
|
|
6e6d052283 | ||
|
|
0d8a35350b | ||
|
|
9bc50d4775 | ||
|
|
4bac85e5e2 | ||
|
|
9868c49c37 | ||
|
|
077454002c | ||
|
|
bc423af462 | ||
|
|
a009536b79 | ||
|
|
c7735d179d | ||
|
|
390e8ffd33 | ||
|
|
422d4548c5 | ||
|
|
f74b35a69a | ||
|
|
69497cad51 | ||
|
|
0e02a74089 | ||
|
|
852d859f93 | ||
|
|
99fed38942 | ||
|
|
1fbe799a56 | ||
|
|
2b5527cbb3 | ||
|
|
88e951fa8c | ||
|
|
cd22e3e682 | ||
|
|
4d28da3aa4 | ||
|
|
51ed62741c | ||
|
|
560a1dc96d | ||
|
|
0fc11d8e88 | ||
|
|
4b0505164f | ||
|
|
02d7b89d2b | ||
|
|
e38b4f87ed | ||
|
|
7bf8900e0f | ||
|
|
5337f2f716 | ||
|
|
d4eae9ce0b | ||
|
|
e12f49660f | ||
|
|
6e4716ef3e |
No files matched your search
@@ -2,9 +2,12 @@ name: Chromatic
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches: [master]
|
||||
branches:
|
||||
- master
|
||||
- big-one-oh
|
||||
|
||||
jobs:
|
||||
chromatic-pr:
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import { Meta, Canvas } from "@storybook/addon-docs";
|
||||
|
||||
<Meta title="ds-react(deprecated)/intro" />
|
||||
|
||||
# Utdaterte pakker
|
||||
|
||||
På sikt vil komponentene under bli flyttet ut av ds-react eller erstattes av andre komponenter
|
||||
|
||||
- PageHeader -> @navikt/ds-react-navno
|
||||
- MicroCard -> @navikt/ds-react-navno
|
||||
|
||||
- StepIndicator -> Horizontal `<Stepper />`
|
||||
@@ -7,7 +7,7 @@ export const parameters = {
|
||||
options: {
|
||||
storySort: {
|
||||
method: "",
|
||||
order: ["Intro", "ds-icons", "ds-react", ["Default"]],
|
||||
order: ["Intro", "ds-icons", "ds-react", ["form"], ["Default"]],
|
||||
locales: "",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
# NAV designsystem Codemods
|
||||
|
||||
Collection of Codemods for easier migration between breaking changes.
|
||||
|
||||
Highly inspired by both MUI and NEXT
|
||||
|
||||
- [NEXT](https://nextjs.org/docs/advanced-features/codemods)
|
||||
- [MUI](https://github.com/mui/material-ui/tree/master/packages/mui-codemod)
|
||||
|
||||
## Usage
|
||||
|
||||
Codemod transformations runs code-transformations programmatically in your project. This helps when migrating lots of breaking changes without spending hours doing it manually.
|
||||
|
||||
NOTE: This codemod only supports fairly "default" usage of components. Components used with dynamic imports, styled-components, non-default imports (example `import * as DS from "@navikt/ds-react"` or `from "@navikt/ds-react/cjs"`) will not work as expected.
|
||||
|
||||
```javascript
|
||||
npx @navikt/ds-codemod <transform> <path>
|
||||
```
|
||||
|
||||
transform - name of transform, see available transforms below.
|
||||
path - files or directory to transform
|
||||
--dry Do a dry-run, no code will be edited
|
||||
--print Prints the changed output for comparison
|
||||
|
||||
## Beta (0.19.x) -> v1.0.0
|
||||
|
||||
`npx @navikt/ds-codemod v1.0.0/preset src`
|
||||
|
||||
### preset
|
||||
|
||||
Combines all avaliable codemods for migrating from beta -> v1.0.0. This transform should only be ran once.
|
||||
|
||||
Includes these transforms
|
||||
|
||||
- v1.0.0/tabs
|
||||
- v1.0.0/chat
|
||||
- v1.0.0/pagination
|
||||
|
||||
### tabs
|
||||
|
||||
`npx @navikt/ds-codemod v1.0.0/tabs src`
|
||||
|
||||
```diff
|
||||
<Tabs
|
||||
defaultValue="logg"
|
||||
onChange={(x) => console.log(x)}
|
||||
- loop
|
||||
+ iconPosition="left"
|
||||
>
|
||||
<Tabs.List
|
||||
- loop
|
||||
>
|
||||
<Tabs.Tab
|
||||
value="logg"
|
||||
label="logg"
|
||||
- iconPosition="left"
|
||||
/>
|
||||
</Tabs.List>
|
||||
<Tabs.Panel value="logg">TabPanel for Logg-tab</Tabs.Panel>
|
||||
</Tabs>
|
||||
```
|
||||
|
||||
### chat
|
||||
|
||||
`npx @navikt/ds-codemodmod v1.0.0/chat src`
|
||||
|
||||
```diff
|
||||
-<SpeechBubble
|
||||
+<Chat
|
||||
- illustration={<Illustration />}
|
||||
- topText="Ola Normann 01.01.21 14:00"
|
||||
- illustrationBgColor="blue"
|
||||
+ avatar={<Illustration />}
|
||||
+ name="Ola Normann 01.01.21 14:00"
|
||||
+ avatarBgColor="blue"
|
||||
backgroundColor="red"
|
||||
>
|
||||
- <SpeechBubble.Bubble>
|
||||
+ <Chat.Bubble>
|
||||
Aute minim nisi sunt mollit duis sunt nulla minim non proident.
|
||||
- </SpeechBubble.Bubble>
|
||||
+ </Chat.Bubble>
|
||||
-</SpeechBubble>
|
||||
+</Chat>
|
||||
```
|
||||
|
||||
### pagination
|
||||
|
||||
`npx @navikt/ds-codemod v1.0.0/pagination src`
|
||||
|
||||
This codemod should only be ran once, since the size-scale will keep decreasing for each subsequent iteration.
|
||||
|
||||
```diff
|
||||
-<Pagiation />
|
||||
+<Pagiation size="small"/>
|
||||
|
||||
-<Pagiation size="medium"/>
|
||||
+<Pagiation size="small"/>
|
||||
|
||||
-<Pagiation size="small"/>
|
||||
+<Pagiation size="xsmall"/>
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
[MIT](https://github.com/navikt/Designsystemet/blob/master/LICENCE)
|
||||
@@ -94,6 +94,18 @@ export function runTransform({ files, flags, transformer }) {
|
||||
}
|
||||
|
||||
const TRANSFORMER_INQUIRER_CHOICES = [
|
||||
{
|
||||
name: "v1.0.0/preset: Runs all codemods for beta to v1 migration",
|
||||
value: "v1.0.0/preset",
|
||||
},
|
||||
{
|
||||
name: "v1.0.0/pagination: Fixes breaking API-changes from <Pagination /> component",
|
||||
value: "v1.0.0/pagination",
|
||||
},
|
||||
{
|
||||
name: "v1.0.0/tabs: Fixes breaking API-changes from <Tabs /> component",
|
||||
value: "v1.0.0/tabs",
|
||||
},
|
||||
{
|
||||
name: "v1.0.0/chat: Fixes breaking API-changes from <SpeechBubble /> (now <Chat/>) component",
|
||||
value: "v1.0.0/chat",
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-codemod",
|
||||
"version": "1.0.2",
|
||||
"private": true,
|
||||
"version": "1.0.0-rc.4",
|
||||
"description": "NAV designsystem codemods",
|
||||
"author": "NAV Designsystem team",
|
||||
"license": "MIT",
|
||||
@@ -49,5 +48,8 @@
|
||||
"sideEffects": false,
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,8 @@ export const Demo = () => {
|
||||
<Pagination size="small" onPageChange={setPage} data-version="v1" />
|
||||
<Pagination size="small" onPageChange={setPage} data-version="v1" />
|
||||
<Pagination size="xsmall" onPageChange={setPage} data-version="v1" />
|
||||
<Pagination onPageChange={setPage} />
|
||||
<Pagination size="small" onPageChange={setPage} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -6,6 +6,8 @@ export const Demo = () => {
|
||||
<Pagination size="small" onPageChange={setPage} data-version="v1" />
|
||||
<Pagination size="small" onPageChange={setPage} data-version="v1" />
|
||||
<Pagination size="xsmall" onPageChange={setPage} data-version="v1" />
|
||||
<Pagination size="small" onPageChange={setPage} data-version="v1" />
|
||||
<Pagination size="xsmall" onPageChange={setPage} data-version="v1" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -3,9 +3,9 @@ import { Pagination as DsPagination, Button } from "@navikt/ds-react";
|
||||
export const Demo = () => {
|
||||
return (
|
||||
<div>
|
||||
<DsPagination size="small" onPageChange={setPage} />
|
||||
<DsPagination size="small" onPageChange={setPage} />
|
||||
<DsPagination size="xsmall" onPageChange={setPage} />
|
||||
<DsPagination size="small" onPageChange={setPage} data-version="v1" />
|
||||
<DsPagination size="small" onPageChange={setPage} data-version="v1" />
|
||||
<DsPagination size="xsmall" onPageChange={setPage} data-version="v1" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -3,9 +3,9 @@ import { Pagination, Button } from "@navikt/ds-react";
|
||||
export const Demo = () => {
|
||||
return (
|
||||
<div>
|
||||
<Pagination size="small" onPageChange={setPage} />
|
||||
<Pagination size="small" onPageChange={setPage} />
|
||||
<Pagination size="xsmall" onPageChange={setPage} />
|
||||
<Pagination size="small" onPageChange={setPage} data-version="v1" />
|
||||
<Pagination size="small" onPageChange={setPage} data-version="v1" />
|
||||
<Pagination size="xsmall" onPageChange={setPage} data-version="v1" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -8,6 +8,22 @@ export default function transformer(file, api, options) {
|
||||
|
||||
const root = j(file.source);
|
||||
|
||||
function addMigrationTag(node) {
|
||||
const attributes = node.openingElement.attributes;
|
||||
const isMigrated = attributes.find(
|
||||
(node) =>
|
||||
node.type === "JSXAttribute" &&
|
||||
node.name.name === "data-version" &&
|
||||
node.value.value === "v1"
|
||||
);
|
||||
|
||||
if (!isMigrated) {
|
||||
attributes.push(
|
||||
j.jsxAttribute(j.jsxIdentifier("data-version"), j.literal("v1"))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/* https://github.com/mui/material-ui/blob/master/packages/mui-codemod/src/v5.0.0/variant-prop.js */
|
||||
function addExplicitStandardProp(node) {
|
||||
const attributes = node.openingElement.attributes;
|
||||
@@ -19,6 +35,7 @@ export default function transformer(file, api, options) {
|
||||
attributes.unshift(
|
||||
j.jsxAttribute(j.jsxIdentifier("size"), j.literal("small"))
|
||||
);
|
||||
addMigrationTag(node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,14 +59,20 @@ export default function transformer(file, api, options) {
|
||||
const skip = !!parent.value.openingElement?.attributes.find(
|
||||
(x) => x.name.name === "data-version" && x.value.value === "v1"
|
||||
);
|
||||
|
||||
parent.value.openingElement?.attributes.forEach((x, index) => {
|
||||
let didUpdate = false;
|
||||
if (x.name?.name === "size" && x.type === "JSXAttribute" && !skip) {
|
||||
/* addExplicitStandardProp */
|
||||
if (x.value.value === "medium") {
|
||||
x.value = j.literal("small");
|
||||
didUpdate = true;
|
||||
} else if (x.value.value === "small") {
|
||||
x.value = j.literal("xsmall");
|
||||
didUpdate = true;
|
||||
}
|
||||
|
||||
didUpdate && addMigrationTag(parent.value);
|
||||
}
|
||||
});
|
||||
addExplicitStandardProp(parent.value);
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import transformTabs from "./tabs";
|
||||
import transformChat from "./chat";
|
||||
import transformPagination from "./pagination";
|
||||
|
||||
/**
|
||||
* @param {import('jscodeshift').FileInfo} file
|
||||
* @param {import('jscodeshift').API} api
|
||||
*/
|
||||
export default function transformer(file, api, options) {
|
||||
file.source = transformTabs(file, api, options);
|
||||
file.source = transformChat(file, api, options);
|
||||
file.source = transformPagination(file, api, options);
|
||||
|
||||
return file.source;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
# NAV designsystem styling
|
||||
|
||||
CSS-package containing design for @navikt/ds-react and more.
|
||||
|
||||
- All our tokens (Colors, border, shadows etc)
|
||||
- Component-styling for `@navikt/ds-react` packages
|
||||
- Normalize.css v8
|
||||
- Fonts
|
||||
|
||||
## Installation
|
||||
|
||||
Install `@navikt/ds-css` with yarn
|
||||
|
||||
```bash
|
||||
yarn add @navikt/ds-css
|
||||
```
|
||||
|
||||
Install `@navikt/ds-css` with npm
|
||||
|
||||
```bash
|
||||
npm install @navikt/ds-css
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Import styling for make it accessible. Only need to do this one place.
|
||||
|
||||
```javascript
|
||||
import "@navikt/ds-css";
|
||||
|
||||
function App() {
|
||||
return <Component />;
|
||||
}
|
||||
```
|
||||
|
||||
## Learn more
|
||||
|
||||
- [Designsystem documentation](https://aksel.nav.no/designsystem)
|
||||
- [Storybook](https://master--5f801fb2aea7820022de2936.chromatic.com/)
|
||||
|
||||
## License
|
||||
|
||||
[MIT](https://github.com/navikt/Designsystemet/blob/master/LICENCE)
|
||||
@@ -11,10 +11,6 @@
|
||||
);
|
||||
}
|
||||
|
||||
.navds-accordion .ReactCollapse--collapse {
|
||||
transition: height 250ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.navds-accordion__item:focus-within {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@@ -34,18 +34,12 @@
|
||||
|
||||
.navds-alert--small {
|
||||
padding: var(--navds-spacing-2) var(--navds-spacing-4);
|
||||
gap: var(--navds-spacing-3);
|
||||
}
|
||||
|
||||
.navds-alert--full-width {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.navds-alert--full-width.navds-alert--small {
|
||||
padding-right: var(--navds-spacing-4);
|
||||
padding-left: var(--navds-spacing-4);
|
||||
}
|
||||
|
||||
.navds-alert > .navds-alert__icon {
|
||||
flex-shrink: 0;
|
||||
font-size: 1.5rem;
|
||||
|
||||
+43
-17
@@ -116,24 +116,35 @@
|
||||
display: inline-flex;
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
background: none;
|
||||
border-radius: var(--navds-border-radius-small);
|
||||
min-width: 48px;
|
||||
padding: var(--navds-spacing-3);
|
||||
padding: var(--navds-spacing-3) var(--navds-spacing-5);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--navds-spacing-2);
|
||||
}
|
||||
|
||||
.navds-button--small {
|
||||
padding-top: 0.375rem;
|
||||
padding-bottom: 0.375rem;
|
||||
min-width: 32px;
|
||||
padding: 0.375rem var(--navds-spacing-3);
|
||||
}
|
||||
|
||||
.navds-button--xsmall {
|
||||
padding: 0.125rem 0.375rem;
|
||||
min-width: 24px;
|
||||
padding: 0.125rem var(--navds-spacing-2);
|
||||
gap: var(--navds-spacing-1);
|
||||
}
|
||||
|
||||
.navds-button--icon-only {
|
||||
padding: var(--navds-spacing-3);
|
||||
}
|
||||
|
||||
.navds-button--small.navds-button--icon-only {
|
||||
padding: 0.375rem;
|
||||
}
|
||||
|
||||
.navds-button--xsmall.navds-button--icon-only {
|
||||
padding: var(--navds-spacing-1);
|
||||
}
|
||||
|
||||
.navds-button:focus {
|
||||
@@ -141,22 +152,37 @@
|
||||
box-shadow: var(--navds-shadow-focus);
|
||||
}
|
||||
|
||||
.navds-button__inner {
|
||||
margin: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--navds-spacing-2);
|
||||
}
|
||||
.navds-button__icon {
|
||||
--navds-button-icon-adjustment: -4px;
|
||||
|
||||
.navds-button svg {
|
||||
font-size: 1.5rem;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.navds-button--small svg,
|
||||
.navds-button--xsmall svg {
|
||||
.navds-button__icon:first-child {
|
||||
margin-left: var(--navds-button-icon-adjustment);
|
||||
}
|
||||
|
||||
.navds-button__icon:last-child {
|
||||
margin-right: var(--navds-button-icon-adjustment);
|
||||
}
|
||||
|
||||
.navds-button__icon:only-child {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.navds-button--small .navds-button__icon {
|
||||
--navds-button-icon-adjustment: -2px;
|
||||
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.navds-button--xsmall .navds-button__icon {
|
||||
--navds-button-icon-adjustment: -2px;
|
||||
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
/*************************
|
||||
* .navds-button--primary *
|
||||
*************************/
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
:root {
|
||||
--navds-card-micro-color-text: var(--navds-semantic-color-text);
|
||||
--navds-card-micro-color-background: var(--navds-global-color-orange-200);
|
||||
--navds-card-micro-color-border: transparent;
|
||||
--navds-card-micro-color-text-hover: var(--navds-semantic-color-link);
|
||||
--navds-card-micro-color-background-hover: var(
|
||||
--navds-semantic-color-component-background-light
|
||||
);
|
||||
--navds-card-micro-color-border-hover: var(
|
||||
--navds-semantic-color-interaction-primary
|
||||
);
|
||||
--navds-card-micro-color-text-focus: var(--navds-semantic-color-link);
|
||||
--navds-card-micro-color-background-focus: var(
|
||||
--navds-semantic-color-component-background-light
|
||||
);
|
||||
--navds-card-micro-color-shadow-focus: var(--navds-semantic-color-focus);
|
||||
--navds-card-micro-color-background-active: var(
|
||||
--navds-semantic-color-interaction-primary
|
||||
);
|
||||
--navds-card-micro-color-text-active: var(
|
||||
--navds-semantic-color-text-inverted
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
MicroCard
|
||||
*/
|
||||
|
||||
.navds-card__micro {
|
||||
border-radius: var(--navds-border-radius-full);
|
||||
color: var(--navds-card-micro-color-text);
|
||||
display: inline-flex;
|
||||
padding: var(--navds-spacing-1) var(--navds-spacing-2);
|
||||
text-decoration: none;
|
||||
background-color: var(--navds-card-micro-color-background);
|
||||
border: 1px solid var(--navds-card-micro-color-border);
|
||||
}
|
||||
|
||||
.navds-card__micro:hover {
|
||||
background-color: var(--navds-card-micro-color-background-hover);
|
||||
border-color: var(--navds-card-micro-color-border-hover);
|
||||
color: var(--navds-card-micro-color-text-hover);
|
||||
}
|
||||
|
||||
.navds-card__micro:focus {
|
||||
background-color: var(--navds-card-micro-color-background-focus);
|
||||
color: var(--navds-card-micro-color-text-focus);
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 3px var(--navds-card-micro-color-shadow-focus);
|
||||
}
|
||||
|
||||
.navds-card__micro:active {
|
||||
background-color: var(--navds-card-micro-color-background-active);
|
||||
color: var(--navds-card-micro-color-text-active);
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
:root {
|
||||
--navds-chat-color-background: var(--navds-semantic-color-canvas-background);
|
||||
--navds-chat-color-avatar: var(--navds-semantic-color-text);
|
||||
--navds-chat-color-avatar-background: var(
|
||||
--navds-semantic-color-canvas-background
|
||||
);
|
||||
}
|
||||
|
||||
.navds-chat {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: var(--navds-spacing-4);
|
||||
padding-right: var(--navds-spacing-16);
|
||||
}
|
||||
|
||||
.navds-chat--right {
|
||||
flex-direction: row-reverse;
|
||||
padding-right: 0;
|
||||
padding-left: var(--navds-spacing-16);
|
||||
}
|
||||
|
||||
.navds-chat__bubble-wrapper {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--navds-spacing-3);
|
||||
}
|
||||
|
||||
.navds-chat--right .navds-chat__bubble-wrapper {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.navds-chat__avatar {
|
||||
align-items: center;
|
||||
background: var(--navds-chat-color-avatar-background);
|
||||
color: var(--navds-chat-color-avatar);
|
||||
border-radius: var(--navds-border-radius-full);
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
height: 3rem;
|
||||
width: 3rem;
|
||||
}
|
||||
|
||||
.navds-chat__avatar svg {
|
||||
align-self: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.navds-chat__bubble {
|
||||
padding: 1rem;
|
||||
box-shadow: var(--navds-shadow-small);
|
||||
width: fit-content;
|
||||
max-width: 37.5rem;
|
||||
background-color: var(--navds-chat-color-background);
|
||||
border-radius: var(--navds-border-radius-xlarge);
|
||||
border-bottom-left-radius: 2px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--navds-spacing-2);
|
||||
}
|
||||
|
||||
.navds-chat__bubble:focus {
|
||||
box-shadow: var(--navds-shadow-focus);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.navds-chat--right .navds-chat__bubble {
|
||||
border-radius: var(--navds-border-radius-xlarge);
|
||||
border-bottom-right-radius: 2px;
|
||||
}
|
||||
|
||||
.navds-chat__top-text {
|
||||
color: var(--navds-semantic-color-text-muted);
|
||||
display: flex;
|
||||
gap: var(--navds-spacing-2);
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.navds-chat--right .navds-chat__top-text {
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.navds-chat__name {
|
||||
font-weight: var(--navds-font-weight-bold);
|
||||
}
|
||||
@@ -37,7 +37,6 @@
|
||||
resize: none;
|
||||
width: 100%;
|
||||
display: block;
|
||||
min-height: 48px;
|
||||
color: var(--navds-text-field-color-text);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
--navds-help-text-color-shadow-hover: var(
|
||||
--navds-semantic-color-interaction-primary
|
||||
);
|
||||
--navds-help-text-color-shadow-focus: var(--navds-semantic-color-focus);
|
||||
--navds-help-text-color-popover-background: var(
|
||||
--navds-semantic-color-feedback-info-background
|
||||
);
|
||||
@@ -35,27 +34,36 @@
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.navds-help-text__button:hover {
|
||||
color: var(--navds-help-text-color-hover);
|
||||
background: var(--navds-help-text-color-background-hover);
|
||||
box-shadow: 0 0 0 2px var(--navds-help-text-color-shadow-hover);
|
||||
}
|
||||
|
||||
.navds-help-text__button:focus {
|
||||
outline: none;
|
||||
color: var(--navds-help-text-color-focus);
|
||||
background: var(--navds-help-text-color-background-focus);
|
||||
box-shadow: 0 0 0 3px var(--navds-help-text-color-shadow-focus);
|
||||
box-shadow: 0 0 0 1px var(--navds-global-color-white),
|
||||
0 0 0 4px var(--navds-semantic-color-focus);
|
||||
}
|
||||
|
||||
.navds-help-text__icon {
|
||||
border-radius: var(--navds-border-radius-full);
|
||||
}
|
||||
|
||||
.navds-help-text__popover > .navds-popover__arrow::before {
|
||||
.navds-help-text__popover > .navds-popover__arrow {
|
||||
background-color: var(--navds-help-text-color-popover-background);
|
||||
}
|
||||
|
||||
.navds-help-text__popover.navds-popover {
|
||||
background-color: var(--navds-help-text-color-popover-background);
|
||||
|
||||
--navds-popover-color-border: rgba(38 38 38 / 0.22);
|
||||
}
|
||||
|
||||
.navds-help-text__icon--filled {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.navds-help-text__button:where(:hover, :focus, [aria-expanded="true"])
|
||||
> .navds-help-text__icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.navds-help-text__button:where(:hover, :focus, [aria-expanded="true"])
|
||||
> .navds-help-text__icon--filled {
|
||||
display: inherit;
|
||||
}
|
||||
@@ -7,12 +7,12 @@
|
||||
@import "alert.css";
|
||||
@import "button.css";
|
||||
@import "content-container.css";
|
||||
@import "chat.css";
|
||||
@import "guide-panel.css";
|
||||
@import "form/index.css";
|
||||
@import "help-text.css";
|
||||
@import "link.css";
|
||||
@import "loader.css";
|
||||
@import "menu.css";
|
||||
@import "modal.css";
|
||||
@import "pagination.css";
|
||||
@import "popover.css";
|
||||
@@ -22,12 +22,6 @@
|
||||
@import "panel.css";
|
||||
@import "link-panel.css";
|
||||
@import "read-more.css";
|
||||
@import "speech-bubble.css";
|
||||
@import "step-indicator.css";
|
||||
@import "stepper.css";
|
||||
@import "table.css";
|
||||
@import "tabs.css";
|
||||
|
||||
/* Navno spesific packages */
|
||||
@import "page-header.css";
|
||||
@import "card.css";
|
||||
@@ -1,102 +0,0 @@
|
||||
:root {
|
||||
--navds-menu-color-text: var(--navds-semantic-color-link);
|
||||
--navds-menu-color-text-hover: var(--navds-semantic-color-text);
|
||||
--navds-menu-color-text-focus: var(--navds-semantic-color-text);
|
||||
--navds-menu-color-background-hover: var(--navds-global-color-blue-50);
|
||||
--navds-menu-color-text-active: var(--navds-semantic-color-text);
|
||||
--navds-menu-outline-focus: var(--navds-semantic-color-focus);
|
||||
}
|
||||
|
||||
.navds-menu__list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
|
||||
.navds-menu__list--inner {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.navds-menu-collapse__button {
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
font: inherit;
|
||||
text-align: inherit;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-weight: var(--navds-font-weight-bold);
|
||||
color: var(--navds-menu-color-text);
|
||||
}
|
||||
|
||||
.navds-menu-item,
|
||||
.navds-menu-collapse__button {
|
||||
font-weight: var(--navds-font-weight-bold);
|
||||
display: flex;
|
||||
padding: var(--navds-spacing-3) var(--navds-spacing-2);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.navds-menu-item:focus,
|
||||
.navds-menu-collapse__button:focus {
|
||||
outline: 3px solid var(--navds-menu-outline-focus);
|
||||
outline-offset: -3px;
|
||||
color: var(--navds-menu-color-text-focus);
|
||||
}
|
||||
|
||||
.navds-menu-collapse__button:focus > .navds-menu-collapse__expand-icon {
|
||||
color: var(--navds-menu-color-focus);
|
||||
}
|
||||
|
||||
.navds-menu-item:hover,
|
||||
.navds-menu-collapse__button:hover {
|
||||
background-color: var(--navds-menu-color-background-hover);
|
||||
color: var(--navds-menu-color-text-hover);
|
||||
}
|
||||
|
||||
.navds-menu-item--active {
|
||||
box-shadow: -0.5rem 0 var(--navds-semantic-color-link);
|
||||
color: var(--navds-menu-color-text-active);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Collapse icon */
|
||||
.navds-menu-collapse__expand-icon {
|
||||
font-size: 1.5rem;
|
||||
flex-shrink: 0;
|
||||
margin-left: var(--navds-spacing-2);
|
||||
}
|
||||
|
||||
.navds-menu-collapse__expand-icon--filled,
|
||||
.navds-menu-collapse__button:hover > .navds-menu-collapse__expand-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.navds-menu-collapse__button:hover > .navds-menu-collapse__expand-icon--filled {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.navds-menu-collapse--open
|
||||
> .navds-menu-collapse__button
|
||||
> .navds-menu-collapse__expand-icon {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
/* Nesting levels */
|
||||
.navds-menu__list[data-depth="1"] .navds-menu-collapse__button,
|
||||
.navds-menu__list[data-depth="1"] .navds-menu-item {
|
||||
padding-left: var(--navds-spacing-6);
|
||||
}
|
||||
|
||||
.navds-menu__list[data-depth="2"] .navds-menu-collapse__button,
|
||||
.navds-menu__list[data-depth="2"] .navds-menu-item {
|
||||
padding-left: var(--navds-spacing-10);
|
||||
}
|
||||
|
||||
.navds-menu__list[data-depth="3"] .navds-menu-collapse__button,
|
||||
.navds-menu__list[data-depth="3"] .navds-menu-item {
|
||||
padding-left: var(--navds-spacing-14);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-css",
|
||||
"version": "0.18.27",
|
||||
"version": "1.0.0-rc.4",
|
||||
"description": "Css for NAV Designsystem components",
|
||||
"author": "NAV Designsystem team",
|
||||
"keywords": [
|
||||
@@ -20,7 +20,7 @@
|
||||
"watch": "postcss --watch --use postcss-import -o dist/index.css index.css"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@navikt/ds-tokens": "^0.9.7",
|
||||
"@navikt/ds-tokens": "^1.0.0-rc.4",
|
||||
"normalize.css": "^8.0.1",
|
||||
"postcss": "^8.3.6",
|
||||
"postcss-cli": "^8.3.1",
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
:root {
|
||||
--navds-page-header-color-background: var(
|
||||
--navds-semantic-color-component-background-light
|
||||
);
|
||||
--navds-page-header-color-shadow-situation: var(
|
||||
--navds-global-color-orange-400
|
||||
);
|
||||
--navds-page-header-color-shadow-product: var(--navds-global-color-green-400);
|
||||
--navds-page-header-color-shadow-guide: var(
|
||||
--navds-global-color-deepblue-400
|
||||
);
|
||||
}
|
||||
|
||||
.navds-page-header {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
background-color: var(--navds-page-header-color-background);
|
||||
padding: 1.5rem calc((100vw - 100%) / 2);
|
||||
margin-left: calc((100vw - 100%) / -2);
|
||||
margin-right: calc((100vw - 100%) / -2);
|
||||
}
|
||||
|
||||
.navds-page-header__wrapper {
|
||||
width: 100%;
|
||||
max-width: 56.5rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.navds-page-header__title {
|
||||
display: flex;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.navds-page-header--left > .navds-page-header__wrapper {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.navds-page-header--center > .navds-page-header__wrapper {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.navds-page-header__illustration {
|
||||
margin-right: 1.5rem;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.navds-page-header__illustration > svg {
|
||||
width: 5rem;
|
||||
height: 5rem;
|
||||
}
|
||||
|
||||
.navds-page-header--situation {
|
||||
box-shadow: 0 -4px 0 var(--navds-page-header-color-shadow-situation) inset;
|
||||
}
|
||||
|
||||
.navds-page-header--product {
|
||||
box-shadow: 0 -4px 0 var(--navds-page-header-color-shadow-product) inset;
|
||||
}
|
||||
|
||||
.navds-page-header--guide {
|
||||
box-shadow: 0 -4px 0 var(--navds-page-header-color-shadow-guide) inset;
|
||||
}
|
||||
|
||||
@media (max-width: 649px) {
|
||||
.navds-page-header__illustration > svg {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
}
|
||||
|
||||
.navds-page-header__title {
|
||||
font-size: var(--navds-font-size-heading-xlarge);
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,12 @@
|
||||
:root {
|
||||
--navds-pagination-color-background-hover: var(
|
||||
--navds-semantic-color-canvas-background
|
||||
);
|
||||
--navds-pagination-color-background-active: var(
|
||||
--navds-semantic-color-interaction-primary-selected
|
||||
);
|
||||
--navds-pagination-color-text-active: var(
|
||||
--navds-semantic-color-text-inverted
|
||||
);
|
||||
--navds-pagination-color-background-selected: var(
|
||||
--navds-semantic-color-interaction-primary
|
||||
--navds-semantic-color-interaction-primary-selected
|
||||
);
|
||||
--navds-pagination-color-text-selected: var(
|
||||
--navds-semantic-color-text-inverted
|
||||
);
|
||||
}
|
||||
|
||||
.navds-pagination {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.navds-pagination__list {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
@@ -32,89 +19,48 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: var(--navds-spacing-8);
|
||||
width: var(--navds-spacing-12);
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.navds-pagination--small .navds-pagination__ellipsis {
|
||||
width: var(--navds-spacing-8);
|
||||
}
|
||||
|
||||
.navds-pagination--xsmall .navds-pagination__ellipsis {
|
||||
width: var(--navds-spacing-6);
|
||||
}
|
||||
|
||||
.navds-pagination__item {
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
background: none;
|
||||
border-radius: var(--navds-border-radius-small);
|
||||
padding: var(--navds-spacing-1) var(--navds-spacing-3);
|
||||
color: var(--navds-semantic-color-text);
|
||||
}
|
||||
|
||||
.navds-pagination--small .navds-pagination__item {
|
||||
padding: 2px var(--navds-spacing-2);
|
||||
}
|
||||
|
||||
.navds-pagination__item:hover {
|
||||
background-color: var(--navds-pagination-color-background-hover);
|
||||
}
|
||||
|
||||
.navds-pagination__item:active {
|
||||
background-color: var(--navds-pagination-color-background-active);
|
||||
color: var(--navds-pagination-color-text-active);
|
||||
}
|
||||
|
||||
.navds-pagination__item:focus {
|
||||
box-shadow: inset 0 0 0 2px var(--navds-semantic-color-focus),
|
||||
inset 0 0 0 3px white;
|
||||
outline: none;
|
||||
.navds-pagination--medium .navds-pagination__item {
|
||||
border-radius: var(--navds-border-radius-medium);
|
||||
}
|
||||
|
||||
.navds-pagination__item[aria-current="true"] {
|
||||
background-color: var(--navds-pagination-color-background-selected);
|
||||
color: var(--navds-pagination-color-text-selected);
|
||||
font-weight: var(--navds-font-weight-bold);
|
||||
}
|
||||
|
||||
.navds-pagination__item[aria-current="true"]:focus {
|
||||
box-shadow: inset 0 0 0 1px var(--navds-button-color-tertiary-border-active),
|
||||
var(--navds-shadow-focus);
|
||||
}
|
||||
|
||||
.navds-pagination__prev-next {
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background: none;
|
||||
text-decoration: underline;
|
||||
color: var(--navds-semantic-color-link);
|
||||
align-items: center;
|
||||
gap: var(--navds-spacing-1);
|
||||
border-radius: var(--navds-border-radius-small);
|
||||
}
|
||||
|
||||
.navds-pagination__prev-next:focus {
|
||||
outline: none;
|
||||
box-shadow: inset 0 0 0 2px var(--navds-semantic-color-focus);
|
||||
.navds-pagination--small .navds-pagination__prev-next {
|
||||
padding: 0.375rem;
|
||||
}
|
||||
|
||||
.navds-pagination__prev-next:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.navds-pagination__prev-next-icon {
|
||||
margin: 7px;
|
||||
}
|
||||
|
||||
.navds-pagination--small .navds-pagination__prev-next-icon {
|
||||
margin: var(--navds-spacing-1);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.navds-pagination__next-text {
|
||||
padding-left: 0.75rem;
|
||||
}
|
||||
|
||||
.navds-pagination__prev-text {
|
||||
padding-right: 0.75rem;
|
||||
.navds-pagination--xsmall .navds-pagination__prev-next {
|
||||
padding: 0.125rem;
|
||||
}
|
||||
|
||||
.navds-pagination--invisible {
|
||||
|
||||
@@ -28,55 +28,33 @@
|
||||
}
|
||||
|
||||
.navds-popover__arrow {
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.navds-popover__arrow::before {
|
||||
display: block;
|
||||
content: "";
|
||||
transform: rotate(45deg);
|
||||
background-color: var(--navds-popover-color-background);
|
||||
border: 1px solid;
|
||||
border-color: var(--navds-popover-color-border);
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.navds-popover[data-popper-placement^="top"] > .navds-popover__arrow {
|
||||
bottom: calc(1rem / -2);
|
||||
}
|
||||
|
||||
.navds-popover[data-popper-placement^="top"] > .navds-popover__arrow::before {
|
||||
.navds-popover[data-placement^="top"] > .navds-popover__arrow {
|
||||
border-left-color: transparent;
|
||||
border-top-color: transparent;
|
||||
}
|
||||
|
||||
.navds-popover[data-popper-placement^="bottom"] > .navds-popover__arrow {
|
||||
top: calc(1rem / -2);
|
||||
}
|
||||
|
||||
/* prettier-ignore */
|
||||
.navds-popover[data-popper-placement^="bottom"] > .navds-popover__arrow::before {
|
||||
.navds-popover[data-placement^="bottom"] > .navds-popover__arrow {
|
||||
border-bottom-color: transparent;
|
||||
border-right-color: transparent;
|
||||
}
|
||||
|
||||
.navds-popover[data-popper-placement^="left"] > .navds-popover__arrow {
|
||||
right: calc(1rem / -2);
|
||||
}
|
||||
|
||||
.navds-popover[data-popper-placement^="left"] > .navds-popover__arrow::before {
|
||||
.navds-popover[data-placement^="left"] > .navds-popover__arrow {
|
||||
border-left-color: transparent;
|
||||
border-bottom-color: transparent;
|
||||
}
|
||||
|
||||
.navds-popover[data-popper-placement^="right"] > .navds-popover__arrow {
|
||||
left: calc(1rem / -2);
|
||||
}
|
||||
|
||||
.navds-popover[data-popper-placement^="right"] > .navds-popover__arrow::before {
|
||||
.navds-popover[data-placement^="right"] > .navds-popover__arrow {
|
||||
border-right-color: transparent;
|
||||
border-top-color: transparent;
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
.navds-read-more {
|
||||
.navds-read-more__button {
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
border: none;
|
||||
background: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--navds-spacing-2);
|
||||
align-items: flex-start;
|
||||
gap: 0.125rem;
|
||||
color: var(--navds-semantic-color-link);
|
||||
border-radius: var(--navds-border-radius-small);
|
||||
padding: var(--navds-spacing-1) var(--navds-spacing-1) var(--navds-spacing-1)
|
||||
@@ -13,44 +13,43 @@
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
.navds-read-more--small {
|
||||
.navds-read-more--small .navds-read-more__button {
|
||||
padding: 2px var(--navds-spacing-1) 2px 2px;
|
||||
}
|
||||
|
||||
.navds-read-more:hover {
|
||||
.navds-read-more__button:hover {
|
||||
background-color: rgba(38 38 38 / 0.06);
|
||||
}
|
||||
|
||||
.navds-read-more:active {
|
||||
.navds-read-more__button:active {
|
||||
background-color: rgba(38 38 38 / 0.1);
|
||||
}
|
||||
|
||||
.navds-read-more:focus {
|
||||
.navds-read-more__button:focus {
|
||||
outline: none;
|
||||
box-shadow: var(--navds-shadow-focus);
|
||||
}
|
||||
|
||||
.navds-read-more + .ReactCollapse--collapse {
|
||||
transition: all 250ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
padding-top: var(--navds-spacing-1);
|
||||
}
|
||||
|
||||
.navds-read-more:not(.navds-read-more--open) + .ReactCollapse--collapse {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.navds-read-more__content {
|
||||
margin-top: var(--navds-spacing-1);
|
||||
border-left: 2px solid rgba(38 38 38 / 0.36);
|
||||
margin-left: 11px;
|
||||
padding-left: 15px;
|
||||
padding-left: 11px;
|
||||
}
|
||||
|
||||
.navds-read-more__expand-icon {
|
||||
font-size: 1.25rem;
|
||||
height: 1.5rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.navds-read-more--open > .navds-read-more__expand-icon {
|
||||
.navds-read-more--small .navds-read-more__expand-icon {
|
||||
height: 1.25rem;
|
||||
}
|
||||
|
||||
.navds-read-more--open
|
||||
> .navds-read-more__button
|
||||
> .navds-read-more__expand-icon {
|
||||
transform: rotate(-180deg);
|
||||
}
|
||||
|
||||
@@ -58,11 +57,11 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.navds-read-more:hover
|
||||
.navds-read-more__button:hover
|
||||
> .navds-read-more__expand-icon.navds-read-more__expand-icon--filled {
|
||||
display: inherit;
|
||||
}
|
||||
|
||||
.navds-read-more:hover > .navds-read-more__expand-icon {
|
||||
.navds-read-more__button:hover > .navds-read-more__expand-icon {
|
||||
display: none;
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
:root {
|
||||
--navds-speechbubble-color-background: var(
|
||||
--navds-semantic-color-component-background-alternate
|
||||
);
|
||||
--navds-speechbubble-color-illustration: var(--navds-semantic-color-text);
|
||||
--navds-speechbubble-color-illustration-background: var(
|
||||
--navds-semantic-color-canvas-background
|
||||
);
|
||||
--navds-speechbubble-color-detail-text: var(
|
||||
--navds-semantic-color-text-muted
|
||||
);
|
||||
}
|
||||
|
||||
.navds-speechbubble {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: var(--navds-spacing-4);
|
||||
}
|
||||
|
||||
.navds-speechbubble--right {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.navds-speechbubble__bubble-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--navds-spacing-3);
|
||||
}
|
||||
|
||||
.navds-speechbubble--right .navds-speechbubble__bubble-list {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.navds-speechbubble__illustration {
|
||||
align-items: center;
|
||||
background: var(--navds-speechbubble-color-illustration-background);
|
||||
color: var(--navds-speechbubble-color-illustration);
|
||||
border-radius: var(--navds-border-radius-full);
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
height: 3rem;
|
||||
width: 3rem;
|
||||
}
|
||||
|
||||
.navds-speechbubble__illustration svg {
|
||||
align-self: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.navds-speechbubble__bubble {
|
||||
padding: 1rem;
|
||||
box-shadow: var(--navds-shadow-small);
|
||||
width: fit-content;
|
||||
max-width: 37.5rem;
|
||||
background-color: var(--navds-speechbubble-color-background);
|
||||
border-radius: var(--navds-border-radius-xlarge);
|
||||
border-bottom-left-radius: 2px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--navds-spacing-2);
|
||||
}
|
||||
|
||||
.navds-speechbubble__bubble:focus {
|
||||
box-shadow: var(--navds-shadow-focus);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.navds-speechbubble--right .navds-speechbubble__bubble {
|
||||
border-radius: var(--navds-border-radius-xlarge);
|
||||
border-bottom-right-radius: 2px;
|
||||
}
|
||||
|
||||
.navds-speechbubble__top-text {
|
||||
color: var(--navds-speechbubble-color-detail-text);
|
||||
}
|
||||
|
||||
.navds-speechbubble--right .navds-speechbubble__top-text {
|
||||
align-self: flex-end;
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
:root {
|
||||
--navds-step-indicator-color-step-line: var(--navds-semantic-color-divider);
|
||||
--navds-step-indicator-color-step-text: var(--navds-semantic-color-text);
|
||||
--navds-step-indicator-color-step-label: var(
|
||||
--navds-semantic-color-interaction-primary
|
||||
);
|
||||
--navds-step-indicator-color-step-number-background: var(
|
||||
--navds-semantic-color-canvas-background-light
|
||||
);
|
||||
--navds-step-indicator-color-step-number-background-finished: var(
|
||||
--navds-semantic-color-canvas-background
|
||||
);
|
||||
--navds-step-indicator-color-step-number-shadow: var(
|
||||
--navds-semantic-color-border
|
||||
);
|
||||
--navds-step-indicator-color-step-number-shadow-active: var(
|
||||
--navds-semantic-color-interaction-primary-selected
|
||||
);
|
||||
--navds-step-indicator-color-step-number-shadow-hover: var(
|
||||
--navds-semantic-color-interaction-primary
|
||||
);
|
||||
--navds-step-indicator-color-step-number-background-active: var(
|
||||
--navds-semantic-color-interaction-primary-selected
|
||||
);
|
||||
--navds-step-indicator-color-step-number-text-active: var(
|
||||
--navds-semantic-color-text-inverted
|
||||
);
|
||||
--navds-step-indicator-color-step-number-shadow-inset: var(
|
||||
--navds-semantic-color-component-background-light
|
||||
);
|
||||
}
|
||||
|
||||
/* StepIndicator */
|
||||
.navds-step-indicator {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Step */
|
||||
.navds-step-indicator__step-wrapper {
|
||||
padding: 0 var(--navds-spacing-2);
|
||||
flex: 1;
|
||||
position: relative;
|
||||
list-style: none;
|
||||
min-width: 10rem;
|
||||
}
|
||||
|
||||
.navds-step-indicator__step-wrapper--hidelabel {
|
||||
min-width: 5rem;
|
||||
}
|
||||
|
||||
.navds-step-indicator__step {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
padding: 0;
|
||||
background: none;
|
||||
border: none;
|
||||
appearance: none;
|
||||
width: 100%;
|
||||
flex-shrink: 1;
|
||||
color: var(--navds-step-indicator-color-step-text);
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.navds-step-indicator__step-number {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
margin: 0.5rem;
|
||||
border-radius: var(--navds-border-radius-full);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 0 0 1px var(--navds-step-indicator-color-step-number-shadow);
|
||||
background-color: var(--navds-step-indicator-color-step-number-background);
|
||||
}
|
||||
|
||||
.navds-step-indicator__step-wrapper:first-child
|
||||
> .navds-step-indicator__step
|
||||
> .navds-step-indicator__step-number {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.navds-step-indicator__step-wrapper:last-child
|
||||
> .navds-step-indicator__step
|
||||
> .navds-step-indicator__step-number {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.navds-step-indicator__step-label {
|
||||
width: 100%;
|
||||
margin-top: var(--navds-spacing-4);
|
||||
text-align: center;
|
||||
color: var(--navds-step-indicator-color-step-label);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.navds-step-indicator__step-line {
|
||||
flex: 1 1 100%;
|
||||
position: absolute;
|
||||
top: 1.5rem;
|
||||
right: calc(50% + 2rem);
|
||||
left: calc(-50% + 2rem);
|
||||
display: block;
|
||||
border-top: 2px solid var(--navds-step-indicator-color-step-line);
|
||||
border-radius: var(--navds-border-radius-medium);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.navds-step-indicator__step--active > .navds-step-indicator__step-label {
|
||||
color: var(--navds-step-indicator-color-step-text);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.navds-step-indicator__step--active > .navds-step-indicator__step-number {
|
||||
background-color: var(
|
||||
--navds-step-indicator-color-step-number-background-active
|
||||
);
|
||||
color: var(--navds-step-indicator-color-step-number-text-active);
|
||||
box-shadow: 0 0 0 1px
|
||||
var(--navds-step-indicator-color-step-number-shadow-active);
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
margin: 0.25rem;
|
||||
}
|
||||
|
||||
.navds-step-indicator__step:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.navds-step-indicator__step:hover > .navds-step-indicator__step-label {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.navds-step-indicator__step.navds-step-indicator__step--disabled:hover
|
||||
> .navds-step-indicator__step-label {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.navds-step-indicator__step--active > .navds-step-indicator__step-label,
|
||||
.navds-step-indicator__step--active.navds-step-indicator__step--disabled:hover
|
||||
> .navds-step-indicator__step-label {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.navds-step-indicator__step:hover > .navds-step-indicator__step-number {
|
||||
background: var(--navds-step-indicator-color-step-number-background);
|
||||
box-shadow: 0 0 0 2px
|
||||
var(--navds-step-indicator-color-step-number-shadow-hover);
|
||||
}
|
||||
|
||||
.navds-step-indicator__step--active:hover > .navds-step-indicator__step-number {
|
||||
background-color: var(
|
||||
--navds-step-indicator-color-step-number-background-active
|
||||
);
|
||||
color: var(--navds-step-indicator-color-step-number-text-active);
|
||||
box-shadow: 0 0 0 1px
|
||||
var(--navds-step-indicator-color-step-number-shadow-active);
|
||||
}
|
||||
|
||||
.navds-step-indicator__step:focus > .navds-step-indicator__step-number {
|
||||
box-shadow: var(--navds-shadow-focus);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.navds-step-indicator__step--active:focus > .navds-step-indicator__step-number {
|
||||
box-shadow: 0 0 0 1px inset
|
||||
var(--navds-step-indicator-color-step-number-shadow-inset),
|
||||
var(--navds-shadow-focus);
|
||||
}
|
||||
|
||||
.navds-step-indicator__step--disabled:hover
|
||||
> .navds-step-indicator__step-number {
|
||||
box-shadow: 0 0 0 1px var(--navds-step-indicator-color-step-number-shadow);
|
||||
}
|
||||
|
||||
.navds-step-indicator__step--disabled.navds-step-indicator__step--active:disabled
|
||||
> .navds-step-indicator__step-number {
|
||||
box-shadow: 0 0 0 1px
|
||||
var(--navds-step-indicator-color-step-number-shadow-active);
|
||||
}
|
||||
|
||||
/* Finished state */
|
||||
.navds-step-indicator__step--finished > .navds-step-indicator__step-number,
|
||||
.navds-step-indicator__step--finished.navds-step-indicator__step--disabled
|
||||
> .navds-step-indicator__step-number {
|
||||
background: var(--navds-step-indicator-color-step-number-background-finished);
|
||||
}
|
||||
|
||||
/* Disabled */
|
||||
.navds-step-indicator__step--disabled > .navds-step-indicator__step-number,
|
||||
.navds-step-indicator__step--disabled > .navds-step-indicator__step-label {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.navds-step-indicator__step--disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
@@ -224,8 +224,7 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.navds-table__expanded-row-collapse {
|
||||
transition: height 250ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
.navds-table__expanded-row-collapse:not([style*="height: 0px;"]) {
|
||||
border-bottom: 1px solid var(--navds-table-cell-color-border);
|
||||
}
|
||||
|
||||
|
||||
@@ -74,6 +74,10 @@
|
||||
gap: var(--navds-spacing-1);
|
||||
}
|
||||
|
||||
.navds-tabs__tab-inner svg {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.navds-tabs__tab-icon--top > .navds-tabs__tab-inner {
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
|
||||
@@ -10,9 +10,6 @@
|
||||
|
||||
.navds-tooltip {
|
||||
z-index: var(--navds-z-index-tooltip);
|
||||
}
|
||||
|
||||
.navds-tooltip__inner {
|
||||
background-color: var(--navds-semantic-color-component-background-inverted);
|
||||
color: var(--navds-semantic-color-text-inverted);
|
||||
border-radius: var(--navds-border-radius-small);
|
||||
|
||||
@@ -1,28 +1,42 @@
|
||||
# @navikt/ds-icons
|
||||
# NAV icons
|
||||
|
||||
Leverer [NAVs ikoner](https://design.nav.no/resources/icons) i React og SVG.
|
||||
All of NAVs own icons (400+ and counting)
|
||||
|
||||
## Installering
|
||||
- As just SVG
|
||||
- As React-components
|
||||
|
||||
```sh
|
||||
// yarn
|
||||
yarn add @navikt/ds-icons
|
||||
## Installation
|
||||
|
||||
// npm
|
||||
npm install @navikt/ds-icons
|
||||
Install `@navikt/ds-icons` with yarn
|
||||
|
||||
```bash
|
||||
yarn add @navikt/ds-icons
|
||||
```
|
||||
|
||||
## Bruk
|
||||
Install `@navikt/ds-icons` with npm
|
||||
|
||||
```jsx
|
||||
/* ESM (anbefalt for tree-shaking)*/
|
||||
import { Down } from "@navikt/ds-icons";
|
||||
/* CJS */
|
||||
import { Down } from "@navikt/cjs/ds-icons";
|
||||
|
||||
<Down aria-label="Åpne meny" />;
|
||||
```bash
|
||||
npm install @navikt/ds-icons
|
||||
```
|
||||
|
||||
## Dokumentasjon
|
||||
## Usage
|
||||
|
||||
- [Ikonsøk](https://design.nav.no/resources/icons)
|
||||
Use as a regular React-component
|
||||
|
||||
```javascript
|
||||
import { Star } from "@navikt/ds-icons";
|
||||
|
||||
function App() {
|
||||
return <Star title="star" />;
|
||||
}
|
||||
```
|
||||
|
||||
## Learn more
|
||||
|
||||
- [Designsystem documentation](https://aksel.nav.no/designsystem)
|
||||
- [Storybook](https://master--5f801fb2aea7820022de2936.chromatic.com/)
|
||||
- [Icon-search](https://aksel.nav.no/designsystem/side/ikoner)
|
||||
|
||||
## License
|
||||
|
||||
[MIT](https://github.com/navikt/Designsystemet/blob/master/LICENCE)
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-icons",
|
||||
"version": "0.8.20",
|
||||
"version": "1.0.0-rc.4",
|
||||
"description": "NAVs egne ikoner i SVG og React format",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -31,19 +31,20 @@
|
||||
"build": "yarn clean && copyfiles util/* src && yarn create-icons && concurrently \"tsc\" \"tsc -p tsconfig.esm.json\" && yarn create-package-json-pointers-to-esm",
|
||||
"update": "ts-node figma/index.ts dotenv/config"
|
||||
},
|
||||
"dependencies": {
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^17.0.30 || ^18.0.0",
|
||||
"react": "^17.0.0 || ^18.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@svgr/cli": "6.2.1",
|
||||
"@types/lodash.startcase": "^4.4.6",
|
||||
"@types/node": "^17.0.35",
|
||||
"@types/rimraf": "3.0.2",
|
||||
"@types/uuid": "^8.3.1",
|
||||
"axios": "^0.21.3",
|
||||
"concurrently": "7.2.1",
|
||||
"copyfiles": "^2.4.1",
|
||||
|
||||
@@ -1,22 +1,39 @@
|
||||
import { useLayoutEffect, useState } from "react";
|
||||
import { v4 } from "uuid";
|
||||
//https://github.com/mui/material-ui/blob/master/packages/mui-utils/src/useId.ts
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
const canUseDOM = (): boolean => {
|
||||
return (
|
||||
typeof window !== "undefined" &&
|
||||
typeof window.document !== "undefined" &&
|
||||
typeof window.document.createElement !== "undefined"
|
||||
);
|
||||
};
|
||||
let globalId = 0;
|
||||
function useGlobalId(idOverride?: string): string | undefined {
|
||||
const [defaultId, setDefaultId] = useState(idOverride);
|
||||
const id = idOverride || defaultId;
|
||||
useEffect(() => {
|
||||
if (defaultId == null) {
|
||||
// Fallback to this default id when possible.
|
||||
// Use the incrementing value for client-side rendering only.
|
||||
// We can't use it server-side.
|
||||
// If you want to use random values please consider the Birthday Problem: https://en.wikipedia.org/wiki/Birthday_problem
|
||||
globalId += 1;
|
||||
setDefaultId(`navds-id-${globalId}`);
|
||||
}
|
||||
}, [defaultId]);
|
||||
return id;
|
||||
}
|
||||
|
||||
export const useClientLayoutEffect = canUseDOM() ? useLayoutEffect : () => {};
|
||||
|
||||
export const useId: (id?: string) => string = (id) => {
|
||||
const [newId, setNewId] = useState<string | undefined>(undefined);
|
||||
|
||||
useClientLayoutEffect(() => {
|
||||
setNewId(v4());
|
||||
}, []);
|
||||
|
||||
return id ?? newId ?? "";
|
||||
};
|
||||
// eslint-disable-next-line no-useless-concat -- Workaround for https://github.com/webpack/webpack/issues/14814
|
||||
const maybeReactUseId: undefined | (() => string) = (React as any)[
|
||||
// eslint-disable-next-line no-useless-concat
|
||||
"useId" + ""
|
||||
];
|
||||
/**
|
||||
*
|
||||
* @example <div id={useId()} />
|
||||
* @param idOverride
|
||||
* @returns {string}
|
||||
*/
|
||||
export function useId(idOverride?: string): string | undefined {
|
||||
if (maybeReactUseId !== undefined) {
|
||||
const reactId = maybeReactUseId();
|
||||
return idOverride ?? reactId;
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks -- `useId` is invariant at runtime.
|
||||
return useGlobalId(idOverride);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
# NAV React-components
|
||||
|
||||
All of NAVs core React-components
|
||||
|
||||
- Uses styling from `@navikt/ds-css`
|
||||
- All components implemented with React.forwardRef!
|
||||
- Most components extend native html-elements.
|
||||
|
||||
## Installation
|
||||
|
||||
Install `@navikt/ds-react` with yarn
|
||||
|
||||
```bash
|
||||
yarn add @navikt/ds-react
|
||||
```
|
||||
|
||||
Install `@navikt/ds-react` with npm
|
||||
|
||||
```bash
|
||||
npm install @navikt/ds-react
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Remember to also import @navikt/ds-css to apply styling!
|
||||
|
||||
```javascript
|
||||
import { Button } from "@navikt/ds-react";
|
||||
|
||||
function App() {
|
||||
return <Button variant="secondary">Click me!</Button>;
|
||||
}
|
||||
```
|
||||
|
||||
## Learn more
|
||||
|
||||
- [Designsystem documentation](https://aksel.nav.no/designsystem)
|
||||
- [Component-docs](https://aksel.nav.no/designsystem/side/oversikt-komponenter)
|
||||
- [Storybook](https://master--5f801fb2aea7820022de2936.chromatic.com/)
|
||||
|
||||
## License
|
||||
|
||||
[MIT](https://github.com/navikt/Designsystemet/blob/master/LICENCE)
|
||||
@@ -1 +1,6 @@
|
||||
import "@testing-library/jest-dom/extend-expect";
|
||||
global.ResizeObserver = jest.fn().mockImplementation(() => ({
|
||||
observe: jest.fn(),
|
||||
unobserve: jest.fn(),
|
||||
disconnect: jest.fn(),
|
||||
}));
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-react",
|
||||
"version": "0.19.25",
|
||||
"version": "1.0.0-rc.4",
|
||||
"description": "NAV designsystem react components",
|
||||
"author": "NAV Designsystem team",
|
||||
"license": "MIT",
|
||||
@@ -35,28 +35,21 @@
|
||||
"test": "jest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@floating-ui/react-dom": "0.6.0",
|
||||
"@material-ui/core": "^4.12.3",
|
||||
"@navikt/ds-icons": "^0.8.20",
|
||||
"@popperjs/core": "^2.10.1",
|
||||
"@floating-ui/react-dom-interactions": "0.6.6",
|
||||
"@navikt/ds-icons": "^1.0.0-rc.4",
|
||||
"@radix-ui/react-tabs": "0.1.5",
|
||||
"@radix-ui/react-toggle-group": "0.1.5",
|
||||
"classnames": "^2.2.6",
|
||||
"react-collapse": "^5.1.0",
|
||||
"react-merge-refs": "^1.1.0",
|
||||
"react-modal": "3.15.1",
|
||||
"react-popper": "^2.2.5",
|
||||
"uuid": "^8.3.2"
|
||||
"clsx": "^1.1.1",
|
||||
"react-modal": "3.15.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/dom": "8.13.0",
|
||||
"@testing-library/jest-dom": "^5.16.0",
|
||||
"@testing-library/react": "^13.0.0",
|
||||
"@testing-library/user-event": "^14.0.0",
|
||||
"@testing-library/react": "^13.3.0",
|
||||
"@testing-library/user-event": "^14.2.0",
|
||||
"@types/faker": "5.5.8",
|
||||
"@types/jest": "^27.0.1",
|
||||
"@types/node": "^17.0.35",
|
||||
"@types/react-collapse": "^5.0.1",
|
||||
"@types/react-modal": "^3.13.1",
|
||||
"concurrently": "7.2.1",
|
||||
"copyfiles": "^2.4.1",
|
||||
@@ -75,5 +68,10 @@
|
||||
"peerDependencies": {
|
||||
"@types/react": "^17.0.30 || ^18.0.0",
|
||||
"react": "^17.0.0 || ^18.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import cl from "classnames";
|
||||
import cl from "clsx";
|
||||
import React, { forwardRef } from "react";
|
||||
import AccordionItem, { AccordionItemType } from "./AccordionItem";
|
||||
import AccordionContent, { AccordionContentType } from "./AccordionContent";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import cl from "classnames";
|
||||
import cl from "clsx";
|
||||
import React, { forwardRef, useContext } from "react";
|
||||
import { Collapse, UnmountClosed } from "react-collapse";
|
||||
import AnimateHeight from "../util/AnimateHeight";
|
||||
import { BodyLong } from "../typography";
|
||||
import { AccordionItemContext } from "./AccordionItem";
|
||||
|
||||
@@ -8,8 +8,6 @@ export interface AccordionContentProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
/**
|
||||
* Content inside Accordion.Content
|
||||
* If renderContentWhenClosed is false in Accordion.Item,
|
||||
* this will be removed from dom when Accordion is closed
|
||||
*/
|
||||
children: React.ReactNode;
|
||||
}
|
||||
@@ -19,7 +17,7 @@ export type AccordionContentType = React.ForwardRefExoticComponent<
|
||||
>;
|
||||
|
||||
const AccordionContent: AccordionContentType = forwardRef(
|
||||
({ children, className, id, ...rest }, ref) => {
|
||||
({ children, className, ...rest }, ref) => {
|
||||
const context = useContext(AccordionItemContext);
|
||||
|
||||
if (context === null) {
|
||||
@@ -29,21 +27,17 @@ const AccordionContent: AccordionContentType = forwardRef(
|
||||
return null;
|
||||
}
|
||||
|
||||
const CollapseComponent = context.renderContentWhenClosed
|
||||
? Collapse
|
||||
: UnmountClosed;
|
||||
|
||||
return (
|
||||
<div ref={ref} aria-labelledby={context.buttonId} {...rest}>
|
||||
<CollapseComponent isOpened={context.open}>
|
||||
<BodyLong
|
||||
as="div"
|
||||
className={cl("navds-accordion__content", className)}
|
||||
>
|
||||
{children}
|
||||
</BodyLong>
|
||||
</CollapseComponent>
|
||||
</div>
|
||||
<AnimateHeight height={context.open ? "auto" : 0} duration={250}>
|
||||
<BodyLong
|
||||
{...rest}
|
||||
as="div"
|
||||
ref={ref}
|
||||
className={cl("navds-accordion__content", className)}
|
||||
>
|
||||
{children}
|
||||
</BodyLong>
|
||||
</AnimateHeight>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Expand, ExpandFilled } from "@navikt/ds-icons";
|
||||
import cl from "classnames";
|
||||
import cl from "clsx";
|
||||
import React, { forwardRef, useContext } from "react";
|
||||
import { Heading } from "..";
|
||||
import { AccordionItemContext } from "./AccordionItem";
|
||||
import { useClientLayoutEffect, useId } from "..";
|
||||
|
||||
export interface AccordionHeaderProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
@@ -17,15 +17,8 @@ export type AccordionHeaderType = React.ForwardRefExoticComponent<
|
||||
>;
|
||||
|
||||
const AccordionHeader: AccordionHeaderType = forwardRef(
|
||||
({ children, className, id, onClick, ...rest }, ref) => {
|
||||
({ children, className, onClick, ...rest }, ref) => {
|
||||
const context = useContext(AccordionItemContext);
|
||||
const newId = useId(id);
|
||||
|
||||
const setButtonId = context && context.setButtonId;
|
||||
|
||||
useClientLayoutEffect(() => {
|
||||
setButtonId && setButtonId(id ? newId : `accordionContent-${newId}`);
|
||||
}, [setButtonId, newId]);
|
||||
|
||||
if (context === null) {
|
||||
console.error(
|
||||
@@ -45,18 +38,18 @@ const AccordionHeader: AccordionHeaderType = forwardRef(
|
||||
<button
|
||||
{...rest}
|
||||
ref={ref}
|
||||
id={context.buttonId}
|
||||
className={cl(
|
||||
"navds-accordion__header",
|
||||
className,
|
||||
"navds-heading",
|
||||
"navds-heading--small"
|
||||
)}
|
||||
className={cl("navds-accordion__header", className)}
|
||||
type="button"
|
||||
onClick={handleClick}
|
||||
aria-expanded={context.open}
|
||||
>
|
||||
<span className="navds-accordion__header-content">{children}</span>
|
||||
<Heading
|
||||
size="small"
|
||||
as="span"
|
||||
className="navds-accordion__header-content"
|
||||
>
|
||||
{children}
|
||||
</Heading>
|
||||
<Expand aria-hidden className="navds-accordion__expand-icon" />
|
||||
<ExpandFilled
|
||||
aria-hidden
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import cl from "classnames";
|
||||
import cl from "clsx";
|
||||
import React, { createContext, forwardRef, useState } from "react";
|
||||
|
||||
export interface AccordionItemProps
|
||||
@@ -18,11 +18,6 @@ export interface AccordionItemProps
|
||||
* @default false
|
||||
*/
|
||||
defaultOpen?: boolean;
|
||||
/**
|
||||
* Removes content inside Accordion.Content if false from dom when closed
|
||||
* @default false
|
||||
*/
|
||||
renderContentWhenClosed?: boolean;
|
||||
}
|
||||
|
||||
export type AccordionItemType = React.ForwardRefExoticComponent<
|
||||
@@ -32,9 +27,6 @@ export type AccordionItemType = React.ForwardRefExoticComponent<
|
||||
export interface AccordionItemContextProps {
|
||||
open: boolean;
|
||||
toggleOpen: () => void;
|
||||
setButtonId: (id: string) => void;
|
||||
buttonId: string;
|
||||
renderContentWhenClosed: boolean;
|
||||
}
|
||||
|
||||
export const AccordionItemContext =
|
||||
@@ -42,20 +34,10 @@ export const AccordionItemContext =
|
||||
|
||||
const AccordionItem: AccordionItemType = forwardRef(
|
||||
(
|
||||
{
|
||||
children,
|
||||
className,
|
||||
open,
|
||||
defaultOpen = false,
|
||||
renderContentWhenClosed = false,
|
||||
onClick,
|
||||
id,
|
||||
...rest
|
||||
},
|
||||
{ children, className, open, defaultOpen = false, onClick, id, ...rest },
|
||||
ref
|
||||
) => {
|
||||
const [internalOpen, setInternalOpen] = useState<boolean>(defaultOpen);
|
||||
const [buttonId, setButtonId] = useState("");
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -73,9 +55,6 @@ const AccordionItem: AccordionItemType = forwardRef(
|
||||
setInternalOpen((iOpen) => !iOpen);
|
||||
}
|
||||
},
|
||||
renderContentWhenClosed,
|
||||
setButtonId,
|
||||
buttonId,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -17,45 +17,45 @@ export default {
|
||||
const Content = () => (
|
||||
<Accordion.Content>
|
||||
Magna aliquip aliquip fugiat nostrud nostrud velit pariatur veniam officia
|
||||
laboris voluptate officia pariatur.Lorem est ex anim velit occaecat nisi qui
|
||||
nostrud sit consectetur consectetur officia nostrud ullamco. Est ex duis
|
||||
proident nostrud elit qui laborum anim minim eu eiusmod. Veniam in nostrud
|
||||
sunt tempor velit incididunt sint ex dolor qui velit id eu. Deserunt magna
|
||||
sunt velit in. Est exercitation id cillum qui do. Minim adipisicing nostrud
|
||||
commodo proident occaecat aliquip nulla anim proident reprehenderit. Magna
|
||||
ipsum officia veniam cupidatat duis veniam dolore reprehenderit mollit
|
||||
velit.Ut consequat commodo minim occaecat id pariatur. Nisi enim tempor
|
||||
laborum commodo. Tempor sit quis nostrud eu cupidatat sunt commodo
|
||||
reprehenderit irure deserunt eiusmod ipsum. Exercitation quis commodo cillum
|
||||
eiusmod eiusmod. Do laborum qui proident commodo adipisicing eiusmod id.
|
||||
laboris voluptate officia pariatur. <a href="#Lorem">Lorem est</a> ex anim
|
||||
velit occaecat nisi qui nostrud sit consectetur consectetur officia nostrud
|
||||
ullamco. Est ex duis proident nostrud elit qui laborum anim minim eu
|
||||
eiusmod. Veniam in nostrud sunt tempor velit incididunt sint ex dolor qui
|
||||
velit id eu. Deserunt magna sunt velit in. Est exercitation id cillum qui
|
||||
do. Minim adipisicing nostrud commodo proident occaecat aliquip nulla anim
|
||||
proident reprehenderit. Magna ipsum officia veniam cupidatat duis veniam
|
||||
dolore reprehenderit mollit velit. Ut consequat commodo minim occaecat id
|
||||
pariatur. Nisi enim tempor laborum commodo. Tempor sit quis nostrud eu
|
||||
cupidatat sunt commodo reprehenderit irure deserunt eiusmod ipsum.
|
||||
Exercitation quis commodo cillum eiusmod eiusmod. Do laborum qui proident
|
||||
commodo adipisicing eiusmod id.
|
||||
</Accordion.Content>
|
||||
);
|
||||
|
||||
export const Default = (props) => {
|
||||
const Item = (props) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const Item = () =>
|
||||
props.controlled ? (
|
||||
<Accordion.Item
|
||||
open={open}
|
||||
renderContentWhenClosed={props.renderContentWhenClosed}
|
||||
>
|
||||
<Accordion.Header onClick={() => setOpen(!open)}>
|
||||
Accordion header text
|
||||
</Accordion.Header>
|
||||
<Content />
|
||||
</Accordion.Item>
|
||||
) : (
|
||||
<Accordion.Item renderContentWhenClosed={props.renderContentWhenClosed}>
|
||||
<Accordion.Header>Accordion header text</Accordion.Header>
|
||||
<Content />
|
||||
</Accordion.Item>
|
||||
);
|
||||
return props.controlled ? (
|
||||
<Accordion.Item open={open}>
|
||||
<Accordion.Header onClick={() => setOpen(!open)}>
|
||||
Accordion header text
|
||||
</Accordion.Header>
|
||||
<Content />
|
||||
</Accordion.Item>
|
||||
) : (
|
||||
<Accordion.Item>
|
||||
<Accordion.Header>Accordion header text</Accordion.Header>
|
||||
<Content />
|
||||
</Accordion.Item>
|
||||
);
|
||||
};
|
||||
|
||||
export const Default = (props) => {
|
||||
return (
|
||||
<div style={{ width: 500 }}>
|
||||
<Accordion>
|
||||
{[...Array(props.nItems ? props.nItems : 2)].map((_, y) => (
|
||||
<Item key={y} />
|
||||
<Item key={y} {...props} />
|
||||
))}
|
||||
</Accordion>
|
||||
</div>
|
||||
@@ -64,7 +64,6 @@ export const Default = (props) => {
|
||||
|
||||
Default.args = {
|
||||
controlled: false,
|
||||
renderContentWhenClosed: false,
|
||||
nItems: 2,
|
||||
};
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
SuccessColored,
|
||||
WarningColored,
|
||||
} from "@navikt/ds-icons";
|
||||
import cl from "classnames";
|
||||
import cl from "clsx";
|
||||
import React, { forwardRef } from "react";
|
||||
import { BodyLong } from "..";
|
||||
|
||||
@@ -23,7 +23,7 @@ export interface AlertProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
*/
|
||||
size?: "medium" | "small";
|
||||
/**
|
||||
* Toggles full-width Alert (removes border-radius)
|
||||
* Removes border-radius
|
||||
* @default false
|
||||
*/
|
||||
fullWidth?: boolean;
|
||||
|
||||
@@ -13,6 +13,12 @@ export default {
|
||||
options: ["error", "warning", "info", "success"],
|
||||
},
|
||||
},
|
||||
size: {
|
||||
control: {
|
||||
type: "radio",
|
||||
options: ["medium", "small"],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -36,6 +42,7 @@ export const Default = (props) => (
|
||||
|
||||
Default.args = {
|
||||
children: "Id elit esse enim reprehenderit enim nisi veniam nostrud.",
|
||||
fullWidth: false,
|
||||
};
|
||||
|
||||
export const Small = () => {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React, { useRef, useState, forwardRef } from "react";
|
||||
import mergeRefs from "react-merge-refs";
|
||||
import cl from "classnames";
|
||||
import { BodyShort, OverridableComponent, Loader } from "../";
|
||||
import React, { useRef, useState, forwardRef, useMemo } from "react";
|
||||
import cl from "clsx";
|
||||
import { OverridableComponent, Loader, mergeRefs, Label } from "../";
|
||||
import { useClientLayoutEffect } from "../util";
|
||||
|
||||
export interface ButtonProps
|
||||
@@ -9,7 +8,7 @@ export interface ButtonProps
|
||||
/**
|
||||
* Button content
|
||||
*/
|
||||
children: React.ReactNode;
|
||||
children?: React.ReactNode;
|
||||
/**
|
||||
* Changes design and interaction-visuals
|
||||
* @default "primary"
|
||||
@@ -30,6 +29,15 @@ export interface ButtonProps
|
||||
* @default false
|
||||
*/
|
||||
loading?: boolean;
|
||||
/**
|
||||
* Button Icon
|
||||
*/
|
||||
icon?: React.ReactNode;
|
||||
/**
|
||||
* Icon position in Button
|
||||
* @default "left"
|
||||
*/
|
||||
iconPosition?: "left" | "right";
|
||||
}
|
||||
|
||||
export const Button: OverridableComponent<ButtonProps, HTMLButtonElement> =
|
||||
@@ -44,14 +52,17 @@ export const Button: OverridableComponent<ButtonProps, HTMLButtonElement> =
|
||||
loading = false,
|
||||
disabled,
|
||||
style,
|
||||
icon,
|
||||
iconPosition = "left",
|
||||
...rest
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const buttonRef = useRef<HTMLButtonElement | null>(null);
|
||||
const mergedRef = mergeRefs([buttonRef, ref]);
|
||||
const [widthOverride, setWidthOverride] = useState<number>();
|
||||
|
||||
const mergedRef = useMemo(() => mergeRefs([buttonRef, ref]), [ref]);
|
||||
|
||||
useClientLayoutEffect(() => {
|
||||
if (loading) {
|
||||
const requestID = window.requestAnimationFrame(() => {
|
||||
@@ -77,6 +88,7 @@ export const Button: OverridableComponent<ButtonProps, HTMLButtonElement> =
|
||||
`navds-button--${size}`,
|
||||
{
|
||||
"navds-button--loading": widthOverride,
|
||||
"navds-button--icon-only": !!icon && !children,
|
||||
}
|
||||
)}
|
||||
style={{
|
||||
@@ -85,14 +97,27 @@ export const Button: OverridableComponent<ButtonProps, HTMLButtonElement> =
|
||||
}}
|
||||
disabled={disabled ?? widthOverride ? true : undefined}
|
||||
>
|
||||
<BodyShort
|
||||
as="span"
|
||||
className="navds-button__inner"
|
||||
size={size === "medium" ? "medium" : "small"}
|
||||
aria-live="polite"
|
||||
>
|
||||
{widthOverride ? <Loader size={size} /> : children}
|
||||
</BodyShort>
|
||||
{widthOverride ? (
|
||||
<Loader size={size} />
|
||||
) : (
|
||||
<>
|
||||
{icon && iconPosition === "left" && (
|
||||
<span className="navds-button__icon">{icon}</span>
|
||||
)}
|
||||
{children && (
|
||||
<Label
|
||||
as="span"
|
||||
size={size === "medium" ? "medium" : "small"}
|
||||
aria-live="polite"
|
||||
>
|
||||
{children}
|
||||
</Label>
|
||||
)}
|
||||
{icon && iconPosition === "right" && (
|
||||
<span className="navds-button__icon">{icon}</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Component>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,13 @@ export default {
|
||||
size: {
|
||||
control: {
|
||||
type: "radio",
|
||||
options: ["medium", "small"],
|
||||
options: ["medium", "small", "xsmall"],
|
||||
},
|
||||
},
|
||||
iconPosition: {
|
||||
control: {
|
||||
type: "radio",
|
||||
options: ["left", "right"],
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -40,10 +46,14 @@ const varSwitch = {
|
||||
|
||||
export const Default = (props) => {
|
||||
return (
|
||||
<Button variant={props.variant} size={props.size} loading={props.loading}>
|
||||
{props.icon ? <Star /> : null}
|
||||
<Button
|
||||
variant={props.variant}
|
||||
size={props.size}
|
||||
loading={props.loading}
|
||||
icon={props.icon ? <Star /> : undefined}
|
||||
iconPosition={props.iconPosition}
|
||||
>
|
||||
{props.children}
|
||||
{props.icon ? <Star /> : null}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
@@ -51,6 +61,7 @@ export const Default = (props) => {
|
||||
Default.args = {
|
||||
icon: false,
|
||||
loading: false,
|
||||
iconPosition: "left",
|
||||
children: "Knapp",
|
||||
};
|
||||
|
||||
@@ -114,26 +125,31 @@ export const Icon = () => (
|
||||
<div className="colgap">
|
||||
<div className="rowgap">
|
||||
{variants.map((variant) => (
|
||||
<Button key={variant} variant={variant}>
|
||||
<Star aria-hidden />
|
||||
<span className="navds-sr-only">Stjerne</span>
|
||||
</Button>
|
||||
<Button
|
||||
key={variant}
|
||||
variant={variant}
|
||||
icon={<Star title="Stjerne" />}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className="rowgap">
|
||||
{variants.map((variant) => (
|
||||
<Button key={variant} variant={variant} size="small">
|
||||
<Star aria-hidden />
|
||||
<span className="navds-sr-only">Stjerne</span>
|
||||
</Button>
|
||||
<Button
|
||||
key={variant}
|
||||
variant={variant}
|
||||
size="small"
|
||||
icon={<Star title="Stjerne" />}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className="rowgap">
|
||||
{variants.map((variant) => (
|
||||
<Button key={variant} variant={variant} size="xsmall">
|
||||
<Star aria-hidden />
|
||||
<span className="navds-sr-only">Stjerne</span>
|
||||
</Button>
|
||||
<Button
|
||||
key={variant}
|
||||
variant={variant}
|
||||
size="xsmall"
|
||||
icon={<Star title="Stjerne" />}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
@@ -142,29 +158,40 @@ export const Icon = () => (
|
||||
export const IconWText = () => (
|
||||
<div className="colgap">
|
||||
<div className="rowgap">
|
||||
{variants.map((variant) => (
|
||||
<Button key={variant} variant={variant}>
|
||||
<Star aria-hidden />
|
||||
{variants.map((variant, i) => (
|
||||
<Button
|
||||
key={variant}
|
||||
variant={variant}
|
||||
icon={<Star title="Stjerne" />}
|
||||
iconPosition={i % 2 ? "left" : "right"}
|
||||
>
|
||||
{varSwitch[variant]}
|
||||
<Star aria-hidden />
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
<div className="rowgap">
|
||||
{variants.map((variant) => (
|
||||
<Button key={variant} variant={variant} size="small">
|
||||
<Star aria-hidden />
|
||||
{variants.map((variant, i) => (
|
||||
<Button
|
||||
key={variant}
|
||||
variant={variant}
|
||||
size="small"
|
||||
icon={<Star title="Stjerne" />}
|
||||
iconPosition={i % 2 ? "left" : "right"}
|
||||
>
|
||||
{varSwitch[variant]}
|
||||
<Star aria-hidden />
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
<div className="rowgap">
|
||||
{variants.map((variant) => (
|
||||
<Button key={variant} variant={variant} size="xsmall">
|
||||
<Star aria-hidden />
|
||||
{variants.map((variant, i) => (
|
||||
<Button
|
||||
key={variant}
|
||||
variant={variant}
|
||||
size="xsmall"
|
||||
icon={<Star title="Stjerne" />}
|
||||
iconPosition={i % 2 ? "left" : "right"}
|
||||
>
|
||||
{varSwitch[variant]}
|
||||
<Star aria-hidden />
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import React, { forwardRef } from "react";
|
||||
import cl from "classnames";
|
||||
import { OverridableComponent } from "..";
|
||||
|
||||
export interface MicroCardProps
|
||||
extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
|
||||
/**
|
||||
* Link text
|
||||
*/
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const MicroCard: OverridableComponent<
|
||||
MicroCardProps,
|
||||
HTMLAnchorElement
|
||||
> = forwardRef(({ className, as: Component = "a", children, ...rest }, ref) => (
|
||||
<Component
|
||||
ref={ref}
|
||||
className={cl(
|
||||
"navds-card__micro",
|
||||
"navds-detail",
|
||||
"navds-detail--small",
|
||||
className
|
||||
)}
|
||||
title={children}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</Component>
|
||||
));
|
||||
|
||||
export default MicroCard;
|
||||
@@ -1,2 +0,0 @@
|
||||
export { default as MicroCard } from "./MicroCard";
|
||||
export { MicroCardProps } from "./MicroCard";
|
||||
@@ -1,26 +0,0 @@
|
||||
import { Meta, Canvas } from "@storybook/addon-docs";
|
||||
import { MicroCard } from "../index.ts";
|
||||
|
||||
<Meta title="ds-react(deprecated)/card/intro" />
|
||||
|
||||
# Hvordan ta i bruk card
|
||||
|
||||
## MicroCard
|
||||
|
||||
Kan brukes som en vanlig lenke direkte:
|
||||
|
||||
```
|
||||
<MicroCard href="/lenke/til/side">Lenketekst</MicroCard>
|
||||
```
|
||||
|
||||
<Canvas>
|
||||
<MicroCard href="/lenke/til/side">Lenketekst</MicroCard>
|
||||
</Canvas>
|
||||
|
||||
Hvis man ønsker å ta i bruk nextjs sin `Link`-komponent:
|
||||
|
||||
```jsx
|
||||
<Link href="/lenke/til/side" passHref>
|
||||
<MicroCard>Lenketekst</MicroCard>
|
||||
</Link>
|
||||
```
|
||||
@@ -1,16 +0,0 @@
|
||||
import React from "react";
|
||||
import { MicroCard } from "../index";
|
||||
import { Meta } from "@storybook/react/types-6-0";
|
||||
export default {
|
||||
title: "ds-react(deprecated)/card",
|
||||
component: MicroCard,
|
||||
} as Meta;
|
||||
|
||||
export const All = () => {
|
||||
return (
|
||||
<div>
|
||||
<h2>MicroCard</h2>
|
||||
<MicroCard href="#">Sit laborum aliqua.</MicroCard>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
import React, { forwardRef, HTMLAttributes } from "react";
|
||||
import cl from "clsx";
|
||||
import { Detail } from "../typography";
|
||||
|
||||
export interface BubbleProps extends HTMLAttributes<HTMLDivElement> {
|
||||
/**
|
||||
* Bubble text
|
||||
*/
|
||||
children: React.ReactNode;
|
||||
/**
|
||||
* name/sender on bubble
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* Timestamp for sent message
|
||||
*/
|
||||
timestamp?: string;
|
||||
/**
|
||||
* Background color on bubble
|
||||
*/
|
||||
backgroundColor?: string;
|
||||
}
|
||||
|
||||
export type BubbleType = React.ForwardRefExoticComponent<
|
||||
BubbleProps & React.RefAttributes<HTMLDivElement>
|
||||
>;
|
||||
|
||||
const Bubble: BubbleType = forwardRef(
|
||||
({ children, className, name, timestamp, backgroundColor, ...rest }, ref) => {
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cl("navds-chat__bubble", className)}
|
||||
style={{ backgroundColor: backgroundColor }}
|
||||
tabIndex={0}
|
||||
{...rest}
|
||||
>
|
||||
{(timestamp || name) && (
|
||||
<div className="navds-chat__top-text">
|
||||
{name && <Detail className="navds-chat__name">{name}</Detail>}
|
||||
{timestamp && (
|
||||
<Detail className="navds-chat__timestamp">{timestamp}</Detail>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default Bubble;
|
||||
@@ -0,0 +1,96 @@
|
||||
import React, { forwardRef, HTMLAttributes } from "react";
|
||||
import cl from "clsx";
|
||||
import Bubble, { BubbleType } from "./Bubble";
|
||||
import { BodyLong, BodyShort } from "..";
|
||||
|
||||
export interface ChatProps extends HTMLAttributes<HTMLDivElement> {
|
||||
/**
|
||||
* Children of type <Chat.Bubble />
|
||||
*/
|
||||
children: React.ReactNode;
|
||||
/**
|
||||
* Chat-message name
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* Timestamp for sent message
|
||||
*/
|
||||
timestamp?: string;
|
||||
/**
|
||||
* Avatar for messenger. Regular text for initials works to
|
||||
*/
|
||||
avatar: React.ReactNode;
|
||||
/**
|
||||
* Background color on bubbles
|
||||
*/
|
||||
backgroundColor?: string;
|
||||
/**
|
||||
* Background color for avatar
|
||||
*/
|
||||
avatarBgColor?: string;
|
||||
/**
|
||||
* Positions avatar and Bubbles
|
||||
* @default "left"
|
||||
*/
|
||||
position?: "left" | "right";
|
||||
}
|
||||
|
||||
interface ChatComponent
|
||||
extends React.ForwardRefExoticComponent<
|
||||
ChatProps & React.RefAttributes<HTMLDivElement>
|
||||
> {
|
||||
Bubble: BubbleType;
|
||||
}
|
||||
|
||||
export const Chat = forwardRef<HTMLDivElement, ChatProps>(
|
||||
(
|
||||
{
|
||||
children,
|
||||
className,
|
||||
name,
|
||||
timestamp,
|
||||
avatar,
|
||||
position = "left",
|
||||
avatarBgColor,
|
||||
backgroundColor,
|
||||
...rest
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cl("navds-chat", className, `navds-chat--${position}`)}
|
||||
{...rest}
|
||||
>
|
||||
<BodyShort
|
||||
as="div"
|
||||
className="navds-chat__avatar"
|
||||
style={{ backgroundColor: avatarBgColor }}
|
||||
>
|
||||
{avatar}
|
||||
</BodyShort>
|
||||
<ol className="navds-chat__bubble-wrapper">
|
||||
{React.Children.map(children, (child, i) => {
|
||||
if (React.isValidElement(child)) {
|
||||
return (
|
||||
<BodyLong as="li">
|
||||
{React.cloneElement(child, {
|
||||
name: name && i === 0 ? name : undefined,
|
||||
timestamp: timestamp && i === 0 ? timestamp : undefined,
|
||||
backgroundColor,
|
||||
...child.props,
|
||||
})}
|
||||
</BodyLong>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</ol>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
) as ChatComponent;
|
||||
|
||||
Chat.Bubble = Bubble;
|
||||
|
||||
export default Chat;
|
||||
@@ -0,0 +1,202 @@
|
||||
import React from "react";
|
||||
import { Chat } from "../index";
|
||||
import { Meta } from "@storybook/react/types-6-0";
|
||||
|
||||
export default {
|
||||
title: "ds-react/Chat",
|
||||
component: Chat,
|
||||
argTypes: {
|
||||
backgroundColor: {
|
||||
control: {
|
||||
type: "text",
|
||||
},
|
||||
},
|
||||
avatarBgColor: {
|
||||
control: {
|
||||
type: "text",
|
||||
},
|
||||
},
|
||||
},
|
||||
} as Meta;
|
||||
|
||||
export const Default = (props) => {
|
||||
return (
|
||||
<Chat
|
||||
avatar={props?.avatar ?? "ON"}
|
||||
name={props?.name ?? "Ola Normann"}
|
||||
timestamp={props?.timestamp ?? "01.01.21 14:00"}
|
||||
avatarBgColor={props?.avatarBgColor}
|
||||
backgroundColor={props?.backgroundColor}
|
||||
>
|
||||
<Chat.Bubble>
|
||||
Aute minim nisi sunt mollit duis sunt nulla minim non proident.
|
||||
</Chat.Bubble>
|
||||
<Chat.Bubble>Tempor fugiat amet eu sint in in ullamco.</Chat.Bubble>
|
||||
<Chat.Bubble>
|
||||
Adipisicing laborum est eu laborum est sit in commodo enim sint laboris
|
||||
labore nisi ut.
|
||||
</Chat.Bubble>
|
||||
</Chat>
|
||||
);
|
||||
};
|
||||
|
||||
Default.args = {
|
||||
name: "Ola Normann",
|
||||
timestamp: "01.01.21 14:00",
|
||||
avatar: "ON",
|
||||
};
|
||||
|
||||
export const Farger = () => {
|
||||
return (
|
||||
<Chat
|
||||
avatar={"ON"}
|
||||
name={"Ola Normann"}
|
||||
timestamp={"01.01.21 14:00"}
|
||||
avatarBgColor={"var(--navds-global-color-lightblue-200)"}
|
||||
backgroundColor={"var(--navds-global-color-lightblue-100)"}
|
||||
>
|
||||
<Chat.Bubble>
|
||||
Aute minim nisi sunt mollit duis sunt nulla minim non proident.
|
||||
</Chat.Bubble>
|
||||
<Chat.Bubble>Tempor fugiat amet eu sint in in ullamco.</Chat.Bubble>
|
||||
<Chat.Bubble>
|
||||
Adipisicing laborum est eu laborum est sit in commodo enim sint laboris
|
||||
labore nisi ut.
|
||||
</Chat.Bubble>
|
||||
</Chat>
|
||||
);
|
||||
};
|
||||
|
||||
export const Position = () => {
|
||||
return (
|
||||
<div className="colgap">
|
||||
<Chat
|
||||
avatar={"ON"}
|
||||
name={"Ola Normann"}
|
||||
timestamp={"01.01.21 14:00"}
|
||||
backgroundColor="rgba(255, 249, 240, 1)"
|
||||
avatarBgColor="rgba(255, 236, 204, 1)"
|
||||
>
|
||||
<Chat.Bubble>
|
||||
Adipisicing laborum est eu laborum est sit in commodo enim sint
|
||||
laboris labore nisi ut.
|
||||
</Chat.Bubble>
|
||||
</Chat>
|
||||
<Chat
|
||||
avatar={"KH"}
|
||||
name={"Kari Høyli"}
|
||||
timestamp={"01.01.21 14:32"}
|
||||
position="right"
|
||||
backgroundColor="rgba(230, 240, 255, 1)"
|
||||
avatarBgColor="rgba(204, 225, 255, 1)"
|
||||
>
|
||||
<Chat.Bubble>
|
||||
Reprehenderit pariatur officia exercitation laboris.
|
||||
</Chat.Bubble>
|
||||
<Chat.Bubble>
|
||||
Enim velit deserunt do quis. Eu fugiat magna esse dolore ad sunt sit
|
||||
est dolore incididunt. Occaecat cupidatat magna.
|
||||
</Chat.Bubble>
|
||||
</Chat>
|
||||
<Chat
|
||||
avatar={"ON"}
|
||||
name={"Ola Normann"}
|
||||
timestamp={"01.01.21 15:00"}
|
||||
backgroundColor="rgba(255, 249, 240, 1)"
|
||||
avatarBgColor="rgba(255, 236, 204, 1)"
|
||||
>
|
||||
<Chat.Bubble>
|
||||
Exercitation irure Lorem cupidatat culpa anim cillum esse ullamco qui
|
||||
dolore laborum et Lorem. Labore sunt duis id Lorem voluptate commodo
|
||||
ea esse. Dolore esse aliqua proident ea ad commodo ut dolore voluptate
|
||||
labore sunt aute.
|
||||
</Chat.Bubble>
|
||||
</Chat>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Avatar = () => {
|
||||
return (
|
||||
<Chat
|
||||
avatar={<Illustration />}
|
||||
name={"Ola Normann"}
|
||||
timestamp={"01.01.21 14:00"}
|
||||
>
|
||||
<Chat.Bubble>
|
||||
Aute minim nisi sunt mollit duis sunt nulla minim non proident.
|
||||
</Chat.Bubble>
|
||||
<Chat.Bubble>Tempor fugiat amet eu sint in in ullamco.</Chat.Bubble>
|
||||
<Chat.Bubble>
|
||||
Adipisicing laborum est eu laborum est sit in commodo enim sint laboris
|
||||
labore nisi ut.
|
||||
</Chat.Bubble>
|
||||
</Chat>
|
||||
);
|
||||
};
|
||||
|
||||
const Illustration = () => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 54 93">
|
||||
<path
|
||||
fill="#e7e5e2"
|
||||
d="M14 50.7C15 52.3 17.9 81 26.5 81S39 51.8 39 50.3c-13.2-7.6-25 .4-25 .4z"
|
||||
/>
|
||||
<path
|
||||
fill="#5c4378"
|
||||
d="M38.7 50.2c6 2.9 15.3 10.9 15.3 18.3V93H0V68.5c0-7.1 8.5-14.8 14.5-18-.3.2-.5.3-.5.3 1 1.7 3.8 9.2 12.4 9.2C35 60 39 51.9 39 50.4c-.1-.1-.2-.2-.3-.2z"
|
||||
/>
|
||||
<path
|
||||
fill="#d2242a"
|
||||
d="M46.7 76H31.2c-.7 0-1.3-.6-1.2-1.3v-8.5c0-.7.6-1.3 1.3-1.3h15.5c.7 0 1.3.6 1.3 1.3v8.5c-.1.7-.7 1.3-1.4 1.3"
|
||||
/>
|
||||
<path
|
||||
fill="#fff"
|
||||
d="M42.9 71c0 2.1-1.7 3.8-3.8 3.8-2.1 0-3.8-1.7-3.8-3.8s1.7-3.8 3.8-3.8c2.1 0 3.8 1.7 3.8 3.8m-8.7 1.7h-.7l.8-1.9h.7l-.8 1.9zm9.3 0H43l.8-1.9h.5l-.8 1.9zm1.2 0h-.2l.8-1.9h.2l-.8 1.9z"
|
||||
/>
|
||||
<path
|
||||
fill="#c52d35"
|
||||
d="M36.2 72.7h.6s.1 0 .1-.1v-1.8s0-.1-.1-.1h-.6s-.1 0-.1.1l-.2.6v.1h.2l.1 1.2c0-.1 0 0 0 0"
|
||||
/>
|
||||
<path
|
||||
fill="#c52d35"
|
||||
d="M37.5 72.7h.6s.1 0 .1-.1v-1.8s0-.1-.1-.1h-.9s-.1 0-.1.1l-.2.6-.1.1h.5c.1 0 .2.1.2.2v1c-.1-.1-.1 0 0 0m2.6-1.9h-.6s-.1 0-.1.1v1.8s0 .1.1.1h.6s.1 0 .1-.1l.2-.6V72h-.2l-.1-1.2"
|
||||
/>
|
||||
<path
|
||||
fill="#c52d35"
|
||||
d="M37.7 72.7h.4s.1 0 .1-.1l.2-.6v-.1h-.2c0 .1-.5.8-.5.8zm3.9-1.9h.7s.1 0 0 .1l-.7 1.8H41l.6-1.9"
|
||||
/>
|
||||
<path
|
||||
fill="#c52d35"
|
||||
d="M40.8 70.8h-1c-.1 0 .3.1.3.1l.7 1.7s0 .1.1.1h.6l-.7-1.9m-1.3.6v.4s-.1-.4-.3-.4c-.3 0-.3.2-.3.3 0 .1.1.3.2.3h.5l-.3.7H39c-.2 0-.9-.3-.9-.9 0-.6.5-1 .9-1 .2-.1.5.2.5.6 0-.1 0-.1 0 0z"
|
||||
/>
|
||||
<path
|
||||
fill="#5a1f57"
|
||||
d="M39.9 66.7h-1.6c-.1 0-.2-.1-.2-.2v-.3c0-.1.1-.2.2-.2h1.6c.1 0 .2.1.2.2v.3c0 .2-.1.2-.2.2"
|
||||
/>
|
||||
<path fill="#c2b5cf" d="M38.7 66.5h.9V64h-.9v2.5z" />
|
||||
<path
|
||||
fill="#e7e5e2"
|
||||
d="M47.2 35.3C44.7 45.6 36.6 53.1 27 53.1S9.3 45.6 6.8 35.3c-.2.1-.5.1-.8.1-1.1 0-2-.8-2-1.7v-7c0-1 .9-1.7 2-1.7h.2C7.7 13.1 16.4 4 27 4c10.6 0 19.3 9.1 20.8 21h.2c1.1 0 2 .8 2 1.7v7c0 1-.9 1.7-2 1.7-.3 0-.5 0-.8-.1z"
|
||||
/>
|
||||
<path
|
||||
fill="#635e59"
|
||||
d="M19 27.6c-1.4.1-1.9-2-1.4-3.4.1-.3.6-1.5 1.4-1.5.8 0 1.2.7 1.3.8.6 1.4.3 4-1.3 4.1m16.2 0c1.4.1 1.9-2 1.4-3.4-.1-.3-.6-1.5-1.4-1.5-.8 0-1.2.7-1.3.8-.6 1.4-.3 4 1.3 4.1"
|
||||
/>
|
||||
<path
|
||||
fill="#d1bfa3"
|
||||
d="M26.8 34.6c-.4 0-.7-.1-1-.2-.3-.1-.4-.4-.3-.7.1-.3.4-.4.7-.3.5.2 1.5.1 2.2-.4.7-.4 1.1-1 1.2-1.5.1-.4-.1-.9-.4-1.3-.2-.2-.8-.2-1.6-.1-.3 0-.5-.1-.6-.4 0-.3.1-.5.4-.6 1.2-.2 2.1 0 2.6.6.5.7.8 1.4.6 2.1-.1.8-.7 1.6-1.7 2.2-.6.3-1.4.6-2.1.6z"
|
||||
/>
|
||||
<path
|
||||
fill="#593a32"
|
||||
d="M27.1 42.1h-.3c-5.3-.2-7.3-4.1-7.4-4.3-.1-.3 0-.6.2-.7.2-.1.6 0 .7.2.1.1 1.9 3.6 6.6 3.8 4.7.2 6.4-3.7 6.4-3.7.1-.3.4-.4.7-.3.3.1.4.4.3.7-.1 0-2.1 4.3-7.2 4.3z"
|
||||
/>
|
||||
<path
|
||||
fill="#f6b873"
|
||||
d="M6.6 30.7c.1-.1.1-.2.1-.3v-2c-.1-5.6 1.8-8.1 3.4-10.1 0 0-1 4.3-.3 3.4 3.8-5 21.4-1.6 25-8.1.5 3.6-4.1 4.6-4.1 4.6 3.7.7 6.9-.8 7.7-2.5.3 1.4-.6 2.4-1.9 3.4 4.5-.9 4.6-4 4.6-4 .6 4.1 5.3 2.5 5.3 9.3v6c0 .3.2.6.5.6h.5c.3 0 .5-.3.5-.6V26c.3-15.6-8.5-26-20.6-26C15.9 0 5 10.4 5 24.1v6.3c0 .4.2.6.5.6h.6c.2 0 .3-.1.5-.3"
|
||||
/>
|
||||
<path
|
||||
fill="#f6b873"
|
||||
d="M25.9 43.4c-4.4 0-8-1.4-8-3.2s3.6-3.2 8-3.2 8 1.4 8 3.2c0 1.8-3.6 3.2-8 3.2m.8-9.4c-2.9 0-4.7.7-8.8 2.1-12.7 4.6-11.6-14-11.6-14C3.4 46 18.6 52 26.5 52c8.1 0 24.1-8.1 21-30 0 0 .4 17.1-12.9 13.8-3.7-.9-5-1.8-7.9-1.8z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
@@ -0,0 +1 @@
|
||||
export { default as Chat, ChatProps } from "./Chat";
|
||||
@@ -1,5 +1,5 @@
|
||||
import cl from "classnames";
|
||||
import React, { forwardRef } from "react";
|
||||
import cl from "clsx";
|
||||
import { BodyLong, Checkbox, CheckboxProps, ErrorMessage } from "..";
|
||||
import { useFormField } from "./useFormField";
|
||||
|
||||
@@ -27,7 +27,7 @@ export interface ConfirmationPanelProps
|
||||
}
|
||||
|
||||
export const ConfirmationPanel = forwardRef<
|
||||
HTMLDivElement,
|
||||
HTMLInputElement,
|
||||
ConfirmationPanelProps
|
||||
>(({ className, children, label, ...props }, ref) => {
|
||||
const { errorId, showErrorMsg, hasError, size, inputProps } = useFormField(
|
||||
@@ -37,7 +37,6 @@ export const ConfirmationPanel = forwardRef<
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cl("navds-confirmation-panel", "navds-form-field", className, {
|
||||
"navds-confirmation-panel--small": size === "small",
|
||||
"navds-confirmation-panel--error": hasError,
|
||||
@@ -53,7 +52,13 @@ export const ConfirmationPanel = forwardRef<
|
||||
{children}
|
||||
</BodyLong>
|
||||
)}
|
||||
<Checkbox {...props} {...inputProps} error={hasError} size={size}>
|
||||
<Checkbox
|
||||
ref={ref}
|
||||
{...props}
|
||||
{...inputProps}
|
||||
error={hasError}
|
||||
size={size}
|
||||
>
|
||||
{label}
|
||||
</Checkbox>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import cl from "classnames";
|
||||
import cl from "clsx";
|
||||
import React, { FieldsetHTMLAttributes, forwardRef, useContext } from "react";
|
||||
import { BodyShort, Detail, Label, ErrorMessage, omit } from "../..";
|
||||
import { FormFieldProps } from "../useFormField";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { forwardRef, SelectHTMLAttributes } from "react";
|
||||
import cl from "classnames";
|
||||
import cl from "clsx";
|
||||
import { Expand } from "@navikt/ds-icons";
|
||||
import { BodyShort, Label, ErrorMessage, omit, Detail } from "..";
|
||||
import { FormFieldProps, useFormField } from "./useFormField";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import cl from "classnames";
|
||||
import cl from "clsx";
|
||||
import React, {
|
||||
forwardRef,
|
||||
InputHTMLAttributes,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { forwardRef, InputHTMLAttributes } from "react";
|
||||
import cl from "classnames";
|
||||
import cl from "clsx";
|
||||
import { BodyShort, Detail, Label, ErrorMessage, omit } from "..";
|
||||
import { FormFieldProps, useFormField } from "./useFormField";
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React, { forwardRef, useState } from "react";
|
||||
import cl from "classnames";
|
||||
import TextareaAutosize from "@material-ui/core/TextareaAutosize";
|
||||
import cl from "clsx";
|
||||
import { BodyShort, Label, ErrorMessage, omit, Detail } from "..";
|
||||
import { FormFieldProps, useFormField } from "./useFormField";
|
||||
import { useId } from "..";
|
||||
import TextareaAutosize from "../util/TextareaAutoSize";
|
||||
|
||||
/**
|
||||
* TODO: Mulighet for lokalisering av sr-only/counter text
|
||||
@@ -68,7 +68,7 @@ export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
const maxLengthId = `TextareaMaxLength-${useId()}`;
|
||||
const maxLengthId = useId();
|
||||
const hasMaxLength = maxLength !== undefined && maxLength > 0;
|
||||
|
||||
const [controlledValue, setControlledValue] = useState<string>(
|
||||
@@ -76,9 +76,9 @@ export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
|
||||
);
|
||||
|
||||
const getMinRows = () => {
|
||||
let rows = rest?.minRows && rest?.minRows >= 3 ? rest?.minRows : 3;
|
||||
let rows = rest?.minRows ? rest?.minRows : 3;
|
||||
if (size === "small") {
|
||||
rows = rest?.minRows && rest?.minRows >= 2 ? rest?.minRows : 2;
|
||||
rows = rest?.minRows ? rest?.minRows : 2;
|
||||
}
|
||||
return rows;
|
||||
};
|
||||
@@ -153,7 +153,7 @@ export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
|
||||
}
|
||||
)}
|
||||
aria-describedby={cl(inputProps["aria-describedby"], {
|
||||
[maxLengthId]: hasMaxLength,
|
||||
[maxLengthId ?? ""]: hasMaxLength,
|
||||
})}
|
||||
/>
|
||||
{hasMaxLength && (
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from "react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { act, fireEvent, render, screen } from "@testing-library/react";
|
||||
import faker from "faker";
|
||||
import { Checkbox, CheckboxGroup } from ".";
|
||||
|
||||
@@ -15,9 +14,8 @@ test("checkbox group chains onChange calls", async () => {
|
||||
const onChange = jest.fn();
|
||||
const value = faker.datatype.string();
|
||||
const label = faker.datatype.string();
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(
|
||||
const { getByLabelText } = render(
|
||||
<CheckboxGroup legend="legend" onChange={onGroupChange}>
|
||||
<Checkbox onChange={onChange} value={value}>
|
||||
{label}
|
||||
@@ -25,7 +23,9 @@ test("checkbox group chains onChange calls", async () => {
|
||||
</CheckboxGroup>
|
||||
);
|
||||
|
||||
await user.click(screen.getByLabelText(label));
|
||||
await act(async () => {
|
||||
fireEvent.click(getByLabelText(label));
|
||||
});
|
||||
|
||||
expect(onGroupChange).toBeCalledTimes(1);
|
||||
expect(onGroupChange).toBeCalledWith([value]);
|
||||
@@ -42,10 +42,14 @@ describe("Checkbox handles controlled-state correctly", () => {
|
||||
);
|
||||
|
||||
test("Checkbox is still checked after click when controlled", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<CheckboxComponent value={["value1", "value2"]} />);
|
||||
await user.click(screen.getByLabelText("label1"));
|
||||
await user.click(screen.getByLabelText("label2"));
|
||||
const { getByLabelText } = render(
|
||||
<CheckboxComponent value={["value1", "value2"]} />
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(getByLabelText("label1"));
|
||||
fireEvent.click(getByLabelText("label2"));
|
||||
});
|
||||
|
||||
expect((screen.getByLabelText("label1") as HTMLInputElement).checked).toBe(
|
||||
true
|
||||
@@ -58,22 +62,21 @@ describe("Checkbox handles controlled-state correctly", () => {
|
||||
|
||||
test("onChange called with expected values", async () => {
|
||||
const onGroupChange = jest.fn();
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(
|
||||
const { getByLabelText } = render(
|
||||
<CheckboxComponent
|
||||
onChange={onGroupChange}
|
||||
value={["value1", "value2"]}
|
||||
/>
|
||||
);
|
||||
|
||||
await user.click(screen.getByLabelText("label1"));
|
||||
await act(async () => {
|
||||
fireEvent.click(getByLabelText("label1"));
|
||||
expect(onGroupChange).lastCalledWith(["value2"]);
|
||||
|
||||
expect(onGroupChange).lastCalledWith(["value2"]);
|
||||
|
||||
await user.click(screen.getByLabelText("label2"));
|
||||
|
||||
expect(onGroupChange).lastCalledWith(["value1"]);
|
||||
fireEvent.click(getByLabelText("label2"));
|
||||
expect(onGroupChange).lastCalledWith(["value1"]);
|
||||
});
|
||||
});
|
||||
|
||||
test("Checkboxes updates after value-prop change", () => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { forwardRef, InputHTMLAttributes } from "react";
|
||||
import cl from "classnames";
|
||||
import cl from "clsx";
|
||||
import useCheckbox from "./useCheckbox";
|
||||
import { FormFieldProps } from "../useFormField";
|
||||
import { BodyShort, Detail, omit } from "../..";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { createContext, forwardRef, useContext, useState } from "react";
|
||||
import cl from "classnames";
|
||||
import cl from "clsx";
|
||||
import { Fieldset, FieldsetProps, FieldsetContext } from "..";
|
||||
|
||||
export interface CheckboxGroupState {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { forwardRef, HTMLAttributes } from "react";
|
||||
import cl from "classnames";
|
||||
import cl from "clsx";
|
||||
import { Heading, BodyShort } from "../..";
|
||||
import ErrorSummaryItem, { ErrorSummaryItemType } from "./ErrorSummaryItem";
|
||||
import { useId } from "../../util";
|
||||
@@ -58,13 +58,13 @@ export const ErrorSummary = forwardRef<HTMLDivElement, ErrorSummaryProps>(
|
||||
tabIndex={-1}
|
||||
aria-live="polite"
|
||||
aria-relevant="all"
|
||||
aria-labelledby={`error-summary-${headingId}`}
|
||||
aria-labelledby={headingId}
|
||||
>
|
||||
<Heading
|
||||
className="navds-error-summary__heading"
|
||||
as={headingTag}
|
||||
size="small"
|
||||
id={`error-summary-${headingId}`}
|
||||
id={headingId}
|
||||
>
|
||||
{heading}
|
||||
</Heading>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { forwardRef } from "react";
|
||||
import cl from "classnames";
|
||||
import cl from "clsx";
|
||||
import { OverridableComponent } from "../..";
|
||||
|
||||
export interface ErrorSummaryItemProps
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { forwardRef, InputHTMLAttributes } from "react";
|
||||
import cl from "classnames";
|
||||
import cl from "clsx";
|
||||
import { BodyShort, Detail, omit } from "../..";
|
||||
import { FormFieldProps } from "../useFormField";
|
||||
import { useRadio } from "./useRadio";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { forwardRef, useContext } from "react";
|
||||
import cl from "classnames";
|
||||
import cl from "clsx";
|
||||
import { Fieldset, FieldsetContext, FieldsetProps } from "..";
|
||||
import { useId } from "../..";
|
||||
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
import { Close, Search as SearchIcon } from "@navikt/ds-icons";
|
||||
import cl from "classnames";
|
||||
import cl from "clsx";
|
||||
import React, {
|
||||
forwardRef,
|
||||
InputHTMLAttributes,
|
||||
useCallback,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import mergeRefs from "react-merge-refs";
|
||||
import { BodyShort, ErrorMessage, Label, omit, useEventListener } from "../..";
|
||||
import {
|
||||
BodyShort,
|
||||
ErrorMessage,
|
||||
Label,
|
||||
mergeRefs,
|
||||
omit,
|
||||
useEventListener,
|
||||
} from "../..";
|
||||
import { FormFieldProps, useFormField } from "../useFormField";
|
||||
import SearchButton, { SearchButtonType } from "./SearchButton";
|
||||
|
||||
@@ -103,7 +110,7 @@ export const Search = forwardRef<HTMLInputElement, SearchProps>(
|
||||
} = props;
|
||||
|
||||
const searchRef = useRef<HTMLInputElement | null>(null);
|
||||
const mergedRef = mergeRefs([searchRef, ref]);
|
||||
const mergedRef = useMemo(() => mergeRefs([searchRef, ref]), [ref]);
|
||||
const [wrapperRef, setWrapperRef] = useState<HTMLDivElement | null>(null);
|
||||
|
||||
const [internalValue, setInternalValue] = useState(defaultValue ?? "");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Search } from "@navikt/ds-icons";
|
||||
import cl from "classnames";
|
||||
import cl from "clsx";
|
||||
import React, { forwardRef, useContext } from "react";
|
||||
import { Button, ButtonProps } from "../..";
|
||||
import { SearchContext } from "./Search";
|
||||
@@ -7,7 +7,7 @@ import { SearchContext } from "./Search";
|
||||
export interface SearchButtonProps
|
||||
extends Omit<ButtonProps, "size" | "children" | "variant"> {
|
||||
/**
|
||||
* Text set before <Search/> icon
|
||||
* Text set after <Search/> icon
|
||||
*/
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
@@ -36,9 +36,13 @@ const SearchButton: SearchButtonType = forwardRef(
|
||||
variant={variant === "secondary" ? "secondary" : "primary"}
|
||||
className={cl("navds-search__button-search", className)}
|
||||
disabled={context?.disabled ?? disabled}
|
||||
icon={
|
||||
<Search
|
||||
{...(children ? { "aria-hidden": true } : { title: "Søk" })}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Search aria-hidden />
|
||||
{children ? children : <span className="navds-sr-only">Søk</span>}
|
||||
{children}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useContext } from "react";
|
||||
import cl from "classnames";
|
||||
import cl from "clsx";
|
||||
import { FieldsetContext } from "./index";
|
||||
import { useId } from "../index";
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { forwardRef, HTMLAttributes } from "react";
|
||||
import cl from "classnames";
|
||||
import cl from "clsx";
|
||||
|
||||
type Column = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
|
||||
export interface CellProps extends HTMLAttributes<HTMLDivElement> {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { forwardRef, HTMLAttributes } from "react";
|
||||
import cl from "classnames";
|
||||
import cl from "clsx";
|
||||
|
||||
export interface ContentContainerProps extends HTMLAttributes<HTMLDivElement> {
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { forwardRef, HTMLAttributes } from "react";
|
||||
import cl from "classnames";
|
||||
import cl from "clsx";
|
||||
|
||||
export interface GridProps extends HTMLAttributes<HTMLDivElement> {
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import * as React from "react";
|
||||
import { BodyLong } from "../..";
|
||||
import { Grid, Cell, ContentContainer } from "../index";
|
||||
|
||||
export default {
|
||||
title: "ds-react/Grid",
|
||||
component: { Grid, Cell },
|
||||
parameters: {
|
||||
layout: "fullscreen",
|
||||
},
|
||||
};
|
||||
|
||||
export const Default = () => {
|
||||
return (
|
||||
<>
|
||||
<ContentContainer>
|
||||
<span>
|
||||
Grid har ikke blitt oppdatert siden 2020 og vil på sikt få større
|
||||
endringer eller deprecates. Vi anbefaler derfor å ikke ta vår
|
||||
grid-løsning i bruk for nå.
|
||||
</span>
|
||||
<Grid>
|
||||
<Cell className="cell" xs={12} sm={6} lg={4}>
|
||||
Kolonne
|
||||
</Cell>
|
||||
<Cell className="cell" xs={12} sm={6} lg={4}>
|
||||
Kolonne
|
||||
</Cell>
|
||||
<Cell className="cell" xs={12} sm={12} lg={4}>
|
||||
Kolonne
|
||||
</Cell>
|
||||
</Grid>
|
||||
<style>{`.cell {padding: 1rem; background: repeating-linear-gradient(
|
||||
45deg,
|
||||
#d6d6d6,
|
||||
#d6d6d6 10px,
|
||||
transparent 10px,
|
||||
transparent 20px
|
||||
);}`}</style>
|
||||
</ContentContainer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const Contentcontainer = () => {
|
||||
return (
|
||||
<>
|
||||
<ContentContainer className="container">
|
||||
<BodyLong size="medium">-- Innhold --</BodyLong>
|
||||
</ContentContainer>
|
||||
<style>{`.container {
|
||||
display: flex;
|
||||
border-radius: var(--navds-border-radius-medium);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-bottom: 1.5rem;
|
||||
min-height: 10rem;
|
||||
background: repeating-linear-gradient(
|
||||
45deg,
|
||||
#d6d6d6,
|
||||
#d6d6d6 10px,
|
||||
transparent 10px,
|
||||
transparent 20px
|
||||
);
|
||||
}`}</style>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,79 +0,0 @@
|
||||
import { Meta, Canvas } from "@storybook/addon-docs";
|
||||
import { Grid, Cell } from "../../index";
|
||||
|
||||
<Meta title="ds-react/grid/intro" />
|
||||
|
||||
# Hvordan ta i bruk Grid
|
||||
|
||||
```jsx
|
||||
<ContentContainer>
|
||||
<Grid>
|
||||
<Cell xs={12} sm={6} lg={4}>
|
||||
Kolonne
|
||||
</Cell>
|
||||
<Cell xs={12} sm={6} lg={4}>
|
||||
Kolonne
|
||||
</Cell>
|
||||
<Cell xs={12} sm={12} lg={4}>
|
||||
Kolonne
|
||||
</Cell>
|
||||
</Grid>
|
||||
</ContentContainer>
|
||||
```
|
||||
|
||||
### ContentContainer
|
||||
|
||||
Denne fungerer som en wrapper som sentrer innholdet og passer på at anbefalt maksbredde ikke blir overskredet.
|
||||
|
||||
Stylingen som blir satt av den:
|
||||
|
||||
```css
|
||||
.navds-content-container {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 79.5rem; # (1272px)
|
||||
padding: var(--navds-spacing-4); # 1rem
|
||||
}
|
||||
|
||||
@media (min-width: 448px) {
|
||||
.navds-content-container {
|
||||
padding: var(--navds-spacing-6); # 1.5rem
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Bredder
|
||||
|
||||
Bredden til hver `Cell` bestemmes av propsene `lg`, `md`, `sm`og `xs`
|
||||
som respektivt styrer disse breakpointene
|
||||
|
||||
```jsx
|
||||
type Column = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
|
||||
|
||||
/**
|
||||
* Cell columns on width < 448px
|
||||
*/
|
||||
xs: Column;
|
||||
|
||||
/**
|
||||
* Cell columns on min-width: 448
|
||||
*/
|
||||
sm?: Column;
|
||||
|
||||
/**
|
||||
* Cell columns on min-width: 648
|
||||
*/
|
||||
md?: Column;
|
||||
|
||||
/**
|
||||
* Cell columns on min-width: 960px
|
||||
*/
|
||||
lg?: Column;
|
||||
```
|
||||
|
||||
Eks `xs={12} lg={4}` vil da si at `Cell` tar opp
|
||||
4/12 av griddet når `width > 960px` og 12/12 av griddet
|
||||
når skjermen er `width < 448px`
|
||||
|
||||
Griddet er dynamiskt, noe som vil si at griddet altids består av 12 columns
|
||||
selv om skjermen er 1920px eller 400px. Innholdes "presses" altså sammen.
|
||||
@@ -1,51 +0,0 @@
|
||||
import * as React from "react";
|
||||
import { BodyLong } from "../..";
|
||||
import { Grid, Cell, ContentContainer } from "../index";
|
||||
import "./styles.css";
|
||||
|
||||
export default {
|
||||
title: "ds-react/grid",
|
||||
component: { Grid, Cell },
|
||||
parameters: {
|
||||
layout: "fullscreen",
|
||||
backgrounds: {
|
||||
default: "NAV gray",
|
||||
values: [
|
||||
{
|
||||
name: "white",
|
||||
value: "#ffffff",
|
||||
},
|
||||
{
|
||||
name: "NAV gray",
|
||||
value: "#f1f1f1",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const All = () => {
|
||||
return (
|
||||
<ContentContainer>
|
||||
<Grid>
|
||||
<Cell className={"navds-story-cell"} xs={12} sm={6} lg={4}>
|
||||
Kolonne
|
||||
</Cell>
|
||||
<Cell className={"navds-story-cell"} xs={12} sm={6} lg={4}>
|
||||
Kolonne
|
||||
</Cell>
|
||||
<Cell className={"navds-story-cell"} xs={12} sm={12} lg={4}>
|
||||
Kolonne
|
||||
</Cell>
|
||||
</Grid>
|
||||
</ContentContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export const Contentcontainer = () => {
|
||||
return (
|
||||
<ContentContainer className={"navds-story-content-container"}>
|
||||
<BodyLong size="medium">-- Innhold --</BodyLong>
|
||||
</ContentContainer>
|
||||
);
|
||||
};
|
||||
@@ -1,21 +0,0 @@
|
||||
.navds-story-cell {
|
||||
border-radius: var(--navds-border-radius-medium);
|
||||
background: white;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.navds-story-content-container {
|
||||
display: flex;
|
||||
border-radius: var(--navds-border-radius-medium);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-bottom: 1.5rem;
|
||||
min-height: 10rem;
|
||||
background: repeating-linear-gradient(
|
||||
45deg,
|
||||
#d6d6d6,
|
||||
#d6d6d6 10px,
|
||||
transparent 10px,
|
||||
transparent 20px
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { forwardRef, HTMLAttributes } from "react";
|
||||
import cl from "classnames";
|
||||
import cl from "clsx";
|
||||
import { DefaultIllustration } from "./Illustration";
|
||||
export interface GuideProps
|
||||
extends Omit<HTMLAttributes<HTMLDivElement>, "children"> {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { forwardRef, HTMLAttributes } from "react";
|
||||
import Guide from "./Guide";
|
||||
import cl from "classnames";
|
||||
import cl from "clsx";
|
||||
|
||||
export interface GuidePanelProps extends HTMLAttributes<HTMLDivElement> {
|
||||
/**
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { Helptext as HelpTextIcon } from "@navikt/ds-icons";
|
||||
import { Placement } from "@popperjs/core";
|
||||
import cl from "classnames";
|
||||
import React, { forwardRef, useRef, useState } from "react";
|
||||
import mergeRefs from "react-merge-refs";
|
||||
import { Popover, PopoverProps } from "..";
|
||||
import { Helptext as HelpTextIcon, HelptextFilled } from "@navikt/ds-icons";
|
||||
import cl from "clsx";
|
||||
import React, { forwardRef, useMemo, useRef, useState } from "react";
|
||||
import { Popover, PopoverProps, mergeRefs } from "..";
|
||||
|
||||
export interface HelpTextProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
@@ -21,7 +19,19 @@ export interface HelpTextProps
|
||||
* Default dialog-placement on open
|
||||
* @default "top"
|
||||
*/
|
||||
placement?: Placement;
|
||||
placement?:
|
||||
| "top"
|
||||
| "bottom"
|
||||
| "right"
|
||||
| "left"
|
||||
| "top-start"
|
||||
| "top-end"
|
||||
| "bottom-start"
|
||||
| "bottom-end"
|
||||
| "right-start"
|
||||
| "right-end"
|
||||
| "left-start"
|
||||
| "left-end";
|
||||
}
|
||||
|
||||
export const HelpText = forwardRef<HTMLButtonElement, HelpTextProps>(
|
||||
@@ -38,7 +48,7 @@ export const HelpText = forwardRef<HTMLButtonElement, HelpTextProps>(
|
||||
ref
|
||||
) => {
|
||||
const buttonRef = useRef<HTMLButtonElement | null>(null);
|
||||
const mergedRef = mergeRefs([buttonRef, ref]);
|
||||
const mergedRef = useMemo(() => mergeRefs([buttonRef, ref]), [ref]);
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
@@ -55,6 +65,10 @@ export const HelpText = forwardRef<HTMLButtonElement, HelpTextProps>(
|
||||
aria-expanded={open}
|
||||
>
|
||||
<HelpTextIcon className="navds-help-text__icon" title={title} />
|
||||
<HelptextFilled
|
||||
className="navds-help-text__icon navds-help-text__icon--filled"
|
||||
title={title}
|
||||
/>
|
||||
</button>
|
||||
<Popover
|
||||
onClose={() => setOpen(false)}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from "react";
|
||||
import { TextField, HelpText } from "..";
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import { HelpText } from "..";
|
||||
|
||||
export default {
|
||||
title: "ds-react/HelpText",
|
||||
@@ -9,6 +9,7 @@ export default {
|
||||
control: {
|
||||
type: "radio",
|
||||
options: [
|
||||
"top",
|
||||
"bottom",
|
||||
"right",
|
||||
"left",
|
||||
@@ -20,9 +21,6 @@ export default {
|
||||
"right-end",
|
||||
"left-start",
|
||||
"left-end",
|
||||
"auto",
|
||||
"auto-start",
|
||||
"auto-end",
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -47,17 +45,15 @@ Default.args = {
|
||||
title: "show tooltip",
|
||||
};
|
||||
|
||||
export const Inline = () => {
|
||||
export const Open = () => {
|
||||
const ref = useRef<HTMLButtonElement | null>(null);
|
||||
useEffect(() => {
|
||||
ref.current && ref.current.click();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<TextField
|
||||
label={
|
||||
<div style={{ display: "flex", gap: 8 }}>
|
||||
Text field label
|
||||
<HelpText title="show tooltip">
|
||||
Id ullamco excepteur elit fugiat labore.
|
||||
</HelpText>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
<HelpText ref={ref} title="show tooltip" strategy="fixed">
|
||||
Id ullamco excepteur elit fugiat labore.
|
||||
</HelpText>
|
||||
);
|
||||
};
|
||||
@@ -8,14 +8,12 @@ export * from "./guide-panel";
|
||||
export * from "./link";
|
||||
export * from "./link-panel";
|
||||
export * from "./loader";
|
||||
export * from "./menu";
|
||||
export * from "./modal";
|
||||
export * from "./pagination";
|
||||
export * from "./panel";
|
||||
export * from "./popover";
|
||||
export * from "./read-more";
|
||||
export * from "./speech-bubble";
|
||||
export * from "./step-indicator";
|
||||
export * from "./chat";
|
||||
export * from "./stepper";
|
||||
export * from "./tag";
|
||||
export * from "./toggle-group";
|
||||
@@ -24,7 +22,3 @@ export * from "./tabs";
|
||||
export * from "./tooltip";
|
||||
export * from "./typography";
|
||||
export * from "./util";
|
||||
|
||||
/* Navno spesific packages */
|
||||
export * from "./card";
|
||||
export * from "./page-header";
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { forwardRef } from "react";
|
||||
import { Panel, OverridableComponent } from "..";
|
||||
import { Next } from "@navikt/ds-icons";
|
||||
import cl from "classnames";
|
||||
import cl from "clsx";
|
||||
import { LinkPanelTitle, LinkPanelTitleType } from "./LinkPanelTitle";
|
||||
import {
|
||||
LinkPanelDescription,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { forwardRef } from "react";
|
||||
import cl from "classnames";
|
||||
import cl from "clsx";
|
||||
import { BodyLong } from "..";
|
||||
|
||||
interface LinkPanelDescriptionProps
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { forwardRef } from "react";
|
||||
import cl from "classnames";
|
||||
import cl from "clsx";
|
||||
import { OverridableComponent } from "..";
|
||||
|
||||
interface LinkPanelTitleProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { forwardRef } from "react";
|
||||
import cl from "classnames";
|
||||
import cl from "clsx";
|
||||
import { OverridableComponent } from "..";
|
||||
|
||||
export interface LinkProps
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { forwardRef, SVGProps } from "react";
|
||||
import cl from "classnames";
|
||||
import cl from "clsx";
|
||||
import { useId } from "..";
|
||||
|
||||
export interface LoaderProps extends SVGProps<SVGSVGElement> {
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
import React, { forwardRef } from "react";
|
||||
import cl from "classnames";
|
||||
import MenuItems from "./MenuItems";
|
||||
|
||||
import Item, { MenuItemType } from "./MenuItem";
|
||||
import Collapse, { MenuCollapseType } from "./MenuCollapse";
|
||||
|
||||
export interface MenuProps extends React.HTMLAttributes<HTMLElement> {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
interface MenuComponent
|
||||
extends React.ForwardRefExoticComponent<
|
||||
MenuProps & React.RefAttributes<HTMLElement>
|
||||
> {
|
||||
Collapse: MenuCollapseType;
|
||||
Item: MenuItemType;
|
||||
}
|
||||
|
||||
export const Menu = forwardRef<HTMLElement, MenuProps>(
|
||||
({ children, className, ...rest }, ref) => (
|
||||
<nav
|
||||
{...rest}
|
||||
role="navigation"
|
||||
ref={ref}
|
||||
className={cl("navds-menu", className)}
|
||||
>
|
||||
<MenuItems>{children}</MenuItems>
|
||||
</nav>
|
||||
)
|
||||
) as MenuComponent;
|
||||
|
||||
Menu.Collapse = Collapse;
|
||||
Menu.Item = Item;
|
||||
|
||||
export default Menu;
|
||||
@@ -1,80 +0,0 @@
|
||||
import React, {
|
||||
createContext,
|
||||
forwardRef,
|
||||
HTMLAttributes,
|
||||
useContext,
|
||||
useState,
|
||||
} from "react";
|
||||
import cl from "classnames";
|
||||
import { Expand, ExpandFilled } from "@navikt/ds-icons";
|
||||
import MenuItems from "./MenuItems";
|
||||
|
||||
export const NestingContext = createContext<{ depth: number }>({ depth: 1 });
|
||||
|
||||
const NestingProvider = ({ children }) => {
|
||||
const context = useContext(NestingContext);
|
||||
|
||||
return (
|
||||
<NestingContext.Provider
|
||||
value={{
|
||||
depth: context.depth + 1,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</NestingContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export interface MenuCollapseProps extends HTMLAttributes<HTMLDivElement> {
|
||||
title: string;
|
||||
defaultOpen?: boolean;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export type MenuCollapseType = React.ForwardRefExoticComponent<
|
||||
MenuCollapseProps & React.RefAttributes<HTMLDivElement>
|
||||
>;
|
||||
|
||||
export const Collapse: MenuCollapseType = forwardRef(
|
||||
({ children, defaultOpen = false, title, className, ...rest }, ref) => {
|
||||
const [isOpen, setIsOpen] = useState(defaultOpen);
|
||||
const context = useContext(NestingContext);
|
||||
|
||||
return (
|
||||
<div
|
||||
{...rest}
|
||||
ref={ref}
|
||||
className={cl("navds-menu-collapse", className, {
|
||||
"navds-menu-collapse--open": isOpen,
|
||||
})}
|
||||
>
|
||||
<button
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
className="navds-menu-collapse__button"
|
||||
>
|
||||
{title}
|
||||
<Expand
|
||||
title={isOpen ? "lukk navigasjons-skuff" : "åpne navigason-skuff"}
|
||||
className="navds-menu-collapse__expand-icon"
|
||||
/>
|
||||
<ExpandFilled
|
||||
title={isOpen ? "lukk navigasjons-skuff" : "åpne navigason-skuff"}
|
||||
className="navds-menu-collapse__expand-icon navds-menu-collapse__expand-icon--filled"
|
||||
/>
|
||||
</button>
|
||||
<NestingProvider>
|
||||
{isOpen && (
|
||||
<MenuItems
|
||||
data-depth={context.depth}
|
||||
className="navds-menu__list--inner"
|
||||
>
|
||||
{children}
|
||||
</MenuItems>
|
||||
)}
|
||||
</NestingProvider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default Collapse;
|
||||
@@ -1,39 +0,0 @@
|
||||
import React, { forwardRef } from "react";
|
||||
import { OverridableComponent } from "..";
|
||||
import cl from "classnames";
|
||||
|
||||
export interface MenuItemProps
|
||||
extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
|
||||
children: React.ReactNode;
|
||||
/**
|
||||
* Sets active styling if true
|
||||
* @default false
|
||||
*/
|
||||
active?: boolean;
|
||||
}
|
||||
|
||||
export type MenuItemType = OverridableComponent<
|
||||
MenuItemProps,
|
||||
HTMLAnchorElement
|
||||
>;
|
||||
|
||||
export const Item: MenuItemType = forwardRef(
|
||||
(
|
||||
{ children, as: Component = "a", active = false, className, ...rest },
|
||||
ref
|
||||
) => {
|
||||
return (
|
||||
<Component
|
||||
ref={ref}
|
||||
className={cl("navds-menu-item", className, {
|
||||
"navds-menu-item--active": active,
|
||||
})}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</Component>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default Item;
|
||||
@@ -1,21 +0,0 @@
|
||||
import cl from "classnames";
|
||||
import React from "react";
|
||||
|
||||
const MenuItems = ({
|
||||
children,
|
||||
className,
|
||||
...rest
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}) => (
|
||||
<ul {...rest} className={cl("navds-menu__list", className)}>
|
||||
{React.Children.toArray(children).map((child, i) => (
|
||||
<li key={i} className="navds-menu__list-item">
|
||||
{child}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
|
||||
export default MenuItems;
|
||||
@@ -1,3 +0,0 @@
|
||||
export { default as Menu, MenuProps } from "./Menu";
|
||||
export { MenuCollapseProps } from "./MenuCollapse";
|
||||
export { MenuItemProps } from "./MenuItem";
|
||||
@@ -1,93 +0,0 @@
|
||||
import { Meta, Canvas } from "@storybook/addon-docs";
|
||||
import { Menu } from "../../index";
|
||||
|
||||
<Meta title="ds-react/menu/intro" />
|
||||
|
||||
# Hvordan ta i bruk Menu
|
||||
|
||||
```jsx
|
||||
<Menu>
|
||||
<Menu.Item href="#leo">Leo</Menu.Item>
|
||||
<Menu.Collapse title="Proin">
|
||||
<Menu.Item href="#nulla" active>
|
||||
Nulla
|
||||
</Menu.Item>
|
||||
<Menu.Item href="#luctus">Luctus</Menu.Item>
|
||||
</Menu.Collapse>
|
||||
<Menu.Collapse title="Accumsan">
|
||||
<Menu.Item href="#justo" aria-current="page">
|
||||
Justo
|
||||
</Menu.Item>
|
||||
<Menu.Collapse title="Proin">
|
||||
<Menu.Item href="#nulla">Nulla</Menu.Item>
|
||||
<Menu.Item href="#luctus">Luctus</Menu.Item>
|
||||
</Menu.Collapse>
|
||||
</Menu.Collapse>
|
||||
</Menu>
|
||||
```
|
||||
|
||||
<Canvas>
|
||||
<Menu>
|
||||
<Menu.Item href="#leo">Leo</Menu.Item>
|
||||
<Menu.Collapse title="Proin">
|
||||
<Menu.Item href="#nulla" active>
|
||||
Nulla
|
||||
</Menu.Item>
|
||||
<Menu.Item href="#luctus">Luctus</Menu.Item>
|
||||
</Menu.Collapse>
|
||||
<Menu.Collapse title="Accumsan">
|
||||
<Menu.Item href="#justo">Justo</Menu.Item>
|
||||
<Menu.Collapse title="Proin">
|
||||
<Menu.Item href="#nulla">Nulla</Menu.Item>
|
||||
<Menu.Item href="#luctus">Luctus</Menu.Item>
|
||||
</Menu.Collapse>
|
||||
</Menu.Collapse>
|
||||
</Menu>
|
||||
</Canvas>
|
||||
|
||||
## Elementer i menyen
|
||||
|
||||
Menyen har to typer elementer man kan bruke: `Collapse` for nesting og `Item` for navigasjonslenker på siden.
|
||||
|
||||
<Canvas>
|
||||
<div>
|
||||
<h2>Item</h2>
|
||||
<Menu>
|
||||
<Menu.Item href="#leo">Lenke</Menu.Item>
|
||||
<Menu.Item href="#leo">Lenke</Menu.Item>
|
||||
<Menu.Item href="#leo">Lenke</Menu.Item>
|
||||
<Menu.Item href="#leo">Lenke</Menu.Item>
|
||||
<Menu.Item href="#leo">Lenke</Menu.Item>
|
||||
</Menu>
|
||||
<h2>Collapses + Item</h2>
|
||||
<Menu>
|
||||
<Menu.Collapse title="Proin">
|
||||
<Menu.Item href="#nulla" active>
|
||||
Nulla
|
||||
</Menu.Item>
|
||||
<Menu.Item href="#luctus">Luctus</Menu.Item>
|
||||
</Menu.Collapse>
|
||||
<Menu.Collapse title="Accumsan">
|
||||
<Menu.Item href="#justo">Justo</Menu.Item>
|
||||
<Menu.Collapse title="Proin">
|
||||
<Menu.Item href="#nulla">Nulla</Menu.Item>
|
||||
<Menu.Item href="#luctus">Luctus</Menu.Item>
|
||||
</Menu.Collapse>
|
||||
</Menu.Collapse>
|
||||
<Menu.Item href="#leo">Lenke</Menu.Item>
|
||||
<Menu.Item href="#leo">Lenke</Menu.Item>
|
||||
</Menu>
|
||||
</div>
|
||||
</Canvas>
|
||||
|
||||
`Menu.Item` er implementert med `OverridableComponent`, noe som
|
||||
gjør at man kan ta i bruk eks react-router slik:
|
||||
|
||||
```jsx
|
||||
<Menu.Item as={NavLink} activeClassName="navds-menu-item--active" to="/1">
|
||||
Lenketekst
|
||||
</Menu.Item>
|
||||
```
|
||||
|
||||
Her er `NavLink` fra `react-router-dom` brukt, og man får da
|
||||
samtidig bruke alle propsene til denne komponenten som da er `activeClassName` her.
|
||||
@@ -1,139 +0,0 @@
|
||||
import * as React from "react";
|
||||
import cl from "classnames";
|
||||
import { Menu } from "../index";
|
||||
import { NavLink, HashRouter as Router } from "react-router-dom";
|
||||
import { MenuItemProps } from "../MenuItem";
|
||||
|
||||
export default {
|
||||
title: "ds-react/menu",
|
||||
component: { Menu },
|
||||
};
|
||||
|
||||
export const All = () => (
|
||||
<div
|
||||
style={{
|
||||
background: "white",
|
||||
maxWidth: 288,
|
||||
}}
|
||||
>
|
||||
<h2>Only Menu.Items</h2>
|
||||
<Menu>
|
||||
<Menu.Item href="#leo">Leo</Menu.Item>
|
||||
|
||||
<Menu.Item href="#justo">Justo</Menu.Item>
|
||||
<Menu.Item href="#pariatur" active aria-current="page">
|
||||
Pariatur
|
||||
</Menu.Item>
|
||||
<Menu.Item href="#nulla">Nulla</Menu.Item>
|
||||
<Menu.Item href="#luctus">Luctus</Menu.Item>
|
||||
<Menu.Item href="#justo">Justo</Menu.Item>
|
||||
<Menu.Item href="#pariatur">Pariatur</Menu.Item>
|
||||
</Menu>
|
||||
|
||||
<h2>With Collapse</h2>
|
||||
<Menu>
|
||||
<Menu.Item href="#leo">Leo</Menu.Item>
|
||||
<Menu.Collapse title="Proin">
|
||||
<Menu.Item href="#nulla" active>
|
||||
Nulla
|
||||
</Menu.Item>
|
||||
<Menu.Item href="#luctus">Luctus</Menu.Item>
|
||||
</Menu.Collapse>
|
||||
<Menu.Collapse title="Accumsan">
|
||||
<Menu.Item href="#justo" aria-current="page">
|
||||
Justo
|
||||
</Menu.Item>
|
||||
<Menu.Item href="#pariatur" active aria-current="page">
|
||||
Pariatur
|
||||
</Menu.Item>
|
||||
<Menu.Collapse title="Proin">
|
||||
<Menu.Item href="#nulla" active>
|
||||
Nulla
|
||||
</Menu.Item>
|
||||
<Menu.Collapse title="Proin">
|
||||
<Menu.Item href="#nulla" active>
|
||||
Nulla
|
||||
</Menu.Item>
|
||||
<Menu.Item href="#luctus">Luctus</Menu.Item>
|
||||
</Menu.Collapse>
|
||||
<Menu.Item href="#luctus">Luctus</Menu.Item>
|
||||
</Menu.Collapse>
|
||||
</Menu.Collapse>
|
||||
<Menu.Item href="#justo" active>
|
||||
Justo
|
||||
</Menu.Item>
|
||||
<Menu.Item href="#pariatur">Pariatur</Menu.Item>
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
|
||||
export const ReactRouter = () => {
|
||||
const MenuItemLink = (props: MenuItemProps & { to: string }) => {
|
||||
const { to } = props;
|
||||
|
||||
const CustomLink = React.useMemo(
|
||||
() => (props: MenuItemProps) => (
|
||||
<NavLink
|
||||
{...props}
|
||||
to={to}
|
||||
className={({ isActive }) =>
|
||||
cl(props.className, {
|
||||
"navds-menu-item--active": isActive,
|
||||
})
|
||||
}
|
||||
/>
|
||||
),
|
||||
[to]
|
||||
);
|
||||
|
||||
return (
|
||||
<Menu.Item {...props} as={CustomLink}>
|
||||
Nivå 2 innrykk 2 rems
|
||||
</Menu.Item>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Menu>
|
||||
<Menu.Collapse title="Nivå 1 - 3 rems høy">
|
||||
<MenuItemLink to="/1">Nivå 2 innrykk 2 rems</MenuItemLink>
|
||||
<MenuItemLink to="/2">Subtitle 3</MenuItemLink>
|
||||
</Menu.Collapse>
|
||||
<Menu.Collapse title="Skjema og søknad">
|
||||
<Menu.Collapse title="Satser">
|
||||
<MenuItemLink to="/3">
|
||||
Nivå 3 - 3 rems innrykk Har du rett til dagpenger når du mottar
|
||||
annen økonomisk støtte fra NAV?
|
||||
</MenuItemLink>
|
||||
</Menu.Collapse>
|
||||
<MenuItemLink to="/4">Subtitle 2</MenuItemLink>
|
||||
<MenuItemLink to="/5">Subtitle 3</MenuItemLink>
|
||||
</Menu.Collapse>
|
||||
<Menu.Collapse title="Sakbehandlingstider">
|
||||
<Menu.Collapse title="Subtitle 4 Lang tittel på andre nivå lorem ipsum">
|
||||
<MenuItemLink to="/6">Subtitle 5</MenuItemLink>
|
||||
</Menu.Collapse>
|
||||
</Menu.Collapse>
|
||||
<MenuItemLink to="/7">Internasjonalt</MenuItemLink>
|
||||
<Menu.Collapse title="Meld fra om endringer lang tittel på første nivå">
|
||||
<MenuItemLink to="/8">Subtitle 6</MenuItemLink>
|
||||
</Menu.Collapse>
|
||||
<MenuItemLink to="/9">Nivå 1</MenuItemLink>
|
||||
</Menu>
|
||||
);
|
||||
};
|
||||
|
||||
ReactRouter.decorators = [
|
||||
(Story) => (
|
||||
<Router>
|
||||
<div
|
||||
style={{
|
||||
background: "white",
|
||||
maxWidth: 288,
|
||||
}}
|
||||
>
|
||||
<Story />
|
||||
</div>
|
||||
</Router>
|
||||
),
|
||||
];
|
||||
@@ -1,9 +1,8 @@
|
||||
import React, { forwardRef, useRef } from "react";
|
||||
import cl from "classnames";
|
||||
import React, { forwardRef, useMemo, useRef } from "react";
|
||||
import cl from "clsx";
|
||||
import ReactModal from "react-modal";
|
||||
import mergeRefs from "react-merge-refs";
|
||||
import { Close } from "@navikt/ds-icons";
|
||||
import { Button } from "..";
|
||||
import { Button, mergeRefs } from "..";
|
||||
import ModalContent, { ModalContentType } from "./ModalContent";
|
||||
|
||||
export interface ModalProps {
|
||||
@@ -77,7 +76,7 @@ export const Modal = forwardRef<ReactModal, ModalProps>(
|
||||
ref
|
||||
) => {
|
||||
const modalRef = useRef<ReactModal | null>(null);
|
||||
const mergedRef = mergeRefs([modalRef, ref]);
|
||||
const mergedRef = useMemo(() => mergeRefs([modalRef, ref]), [ref]);
|
||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
const onModalCloseRequest = (e) => {
|
||||
@@ -114,9 +113,8 @@ export const Modal = forwardRef<ReactModal, ModalProps>(
|
||||
variant="tertiary"
|
||||
ref={buttonRef}
|
||||
onClick={onClose}
|
||||
>
|
||||
<Close title="Lukk modalvindu" />
|
||||
</Button>
|
||||
icon={<Close title="Lukk modalvindu" />}
|
||||
/>
|
||||
)}
|
||||
</ReactModal>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { forwardRef } from "react";
|
||||
import cl from "classnames";
|
||||
import cl from "clsx";
|
||||
|
||||
export interface ModalContentProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
import React, { forwardRef, HTMLAttributes } from "react";
|
||||
import cl from "classnames";
|
||||
import { BodyShort, Heading } from "..";
|
||||
|
||||
export interface PageHeaderProps extends HTMLAttributes<HTMLDivElement> {
|
||||
/**
|
||||
* PageHeader title
|
||||
*/
|
||||
children: string;
|
||||
/**
|
||||
* Pictogram placed in PageHeader
|
||||
*/
|
||||
illustration?: React.ReactNode;
|
||||
/**
|
||||
* Short text placed under title
|
||||
*/
|
||||
description?: string;
|
||||
/**
|
||||
* Predefined variants for PageHeader
|
||||
* @default "guide"
|
||||
*/
|
||||
variant?: "situation" | "product" | "guide";
|
||||
/**
|
||||
* Decides how to align content
|
||||
* @default "left"
|
||||
*/
|
||||
align?: "left" | "center";
|
||||
}
|
||||
|
||||
const PageHeader = forwardRef<HTMLDivElement, PageHeaderProps>(
|
||||
(
|
||||
{
|
||||
children,
|
||||
className,
|
||||
illustration,
|
||||
description,
|
||||
variant = "guide",
|
||||
align = "left",
|
||||
...rest
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cl(
|
||||
"navds-page-header",
|
||||
className,
|
||||
`navds-page-header--${variant}`,
|
||||
`navds-page-header--${align}`
|
||||
)}
|
||||
{...rest}
|
||||
>
|
||||
{illustration && (
|
||||
<div className="navds-page-header__illustration">{illustration}</div>
|
||||
)}
|
||||
<div className="navds-page-header__wrapper">
|
||||
<Heading className="navds-page-header__title" size="xlarge" level="1">
|
||||
{children}
|
||||
</Heading>
|
||||
{description && (
|
||||
<BodyShort className="navds-page-header__description">
|
||||
{description}
|
||||
</BodyShort>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default PageHeader;
|
||||
@@ -1,2 +0,0 @@
|
||||
export { default as PageHeader } from "./PageHeader";
|
||||
export { PageHeaderProps } from "./PageHeader";
|
||||
@@ -1,81 +0,0 @@
|
||||
import { Meta, Canvas } from "@storybook/addon-docs";
|
||||
import { PageHeader } from "../index";
|
||||
import { illustrationPictogram } from "./pictogram";
|
||||
|
||||
<Meta title="ds-react(deprecated)/page-header/intro" />
|
||||
|
||||
# Hvordan ta i bruk PageHeader
|
||||
|
||||
Denne PageHeader-komponenten er utviklet i sammenheng med nye produktsider på NAV og
|
||||
vil sannsynligvis forgå en del endringer før de er ferdige.
|
||||
|
||||
```jsx
|
||||
<PageHeader description="LABORIS AUTE DESERUNT ID AUTE OFFICIA SINT SUNT FUGIAT EX ET CUPIDATAT CONSEQUAT DOLORE.">
|
||||
AUTE OFFICIA SINT SUNT FUGIAT
|
||||
</PageHeader>
|
||||
```
|
||||
|
||||
<Canvas>
|
||||
<PageHeader description="LABORIS AUTE DESERUNT ID AUTE OFFICIA SINT SUNT FUGIAT EX ET CUPIDATAT CONSEQUAT DOLORE.">
|
||||
AUTE OFFICIA SINT SUNT FUGIAT
|
||||
</PageHeader>
|
||||
</Canvas>
|
||||
|
||||
## Med illustrasjon
|
||||
|
||||
Illustrasjonene brukt i PageHeader er "Pictogram" som er designet for den sepsifikke siden og
|
||||
man skal da ikke bare velge et fra figma vilkårlig.
|
||||
|
||||
<Canvas>
|
||||
<PageHeader
|
||||
illustration={illustrationPictogram}
|
||||
description="LABORIS AUTE DESERUNT ID AUTE OFFICIA SINT SUNT FUGIAT EX ET CUPIDATAT CONSEQUAT DOLORE."
|
||||
>
|
||||
AUTE OFFICIA SINT SUNT FUGIAT
|
||||
</PageHeader>
|
||||
</Canvas>
|
||||
|
||||
## Temaer
|
||||
|
||||
Headeren kommer i tre tema: `guide`, `product` og `situation`
|
||||
|
||||
<Canvas>
|
||||
<PageHeader
|
||||
description="LABORIS AUTE DESERUNT ID AUTE OFFICIA SINT SUNT FUGIAT EX ET CUPIDATAT CONSEQUAT DOLORE."
|
||||
variant="guide"
|
||||
>
|
||||
AUTE OFFICIA SINT SUNT FUGIAT
|
||||
</PageHeader>
|
||||
<PageHeader
|
||||
description="LABORIS AUTE DESERUNT ID AUTE OFFICIA SINT SUNT FUGIAT EX ET CUPIDATAT CONSEQUAT DOLORE."
|
||||
variant="product"
|
||||
>
|
||||
AUTE OFFICIA SINT SUNT FUGIAT
|
||||
</PageHeader>
|
||||
<PageHeader
|
||||
description="LABORIS AUTE DESERUNT ID AUTE OFFICIA SINT SUNT FUGIAT EX ET CUPIDATAT CONSEQUAT DOLORE."
|
||||
variant="situation"
|
||||
>
|
||||
AUTE OFFICIA SINT SUNT FUGIAT
|
||||
</PageHeader>
|
||||
</Canvas>
|
||||
|
||||
## Sentrert
|
||||
|
||||
Innhold kan sentreres med `align="center"`-prop
|
||||
|
||||
<Canvas>
|
||||
<PageHeader
|
||||
description="LABORIS AUTE DESERUNT ID AUTE OFFICIA SINT SUNT FUGIAT EX ET CUPIDATAT CONSEQUAT DOLORE."
|
||||
align="center"
|
||||
>
|
||||
AUTE OFFICIA SINT SUNT FUGIAT
|
||||
</PageHeader>
|
||||
<PageHeader
|
||||
illustration={illustrationPictogram}
|
||||
description="LABORIS AUTE DESERUNT ID AUTE OFFICIA SINT SUNT FUGIAT EX ET CUPIDATAT CONSEQUAT DOLORE."
|
||||
align="center"
|
||||
>
|
||||
AUTE OFFICIA SINT SUNT FUGIAT
|
||||
</PageHeader>
|
||||
</Canvas>
|
||||
Loaded 100 of 202 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user