Merge branch 'master' into canon-use-style
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/canon': patch
|
||||
---
|
||||
|
||||
We are consolidating all css files into a single styles.css in Canon.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/canon': patch
|
||||
---
|
||||
|
||||
Add new `RadioGroup` + `Radio` component to Canon
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/backend-defaults': patch
|
||||
'@backstage/backend-test-utils': minor
|
||||
---
|
||||
|
||||
Add a standard `toString` on credentials objects
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend': patch
|
||||
---
|
||||
|
||||
Replaced deprecated uses of `@backstage/backend-common` with the equivalents in `@backstage/backend-defaults` and `@backstage/backend-plugin-api`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/canon': patch
|
||||
---
|
||||
|
||||
Added placeholder prop to TextField component.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/integration-react': patch
|
||||
---
|
||||
|
||||
Separated gitlab `write_repository` and `api` scope to pass checks in `RefreshingAuthSessionManager`
|
||||
File diff suppressed because one or more lines are too long
@@ -5,10 +5,8 @@ const { bundle } = require('lightningcss');
|
||||
const source = '../../packages/canon/src/css';
|
||||
const destination = '../public';
|
||||
|
||||
const source1 = path.join(__dirname, `${source}/core.css`);
|
||||
const destination1 = path.join(__dirname, `${destination}/core.css`);
|
||||
const source2 = path.join(__dirname, `${source}/components.css`);
|
||||
const destination2 = path.join(__dirname, `${destination}/components.css`);
|
||||
const source1 = path.join(__dirname, `${source}/styles.css`);
|
||||
const destination1 = path.join(__dirname, `${destination}/styles.css`);
|
||||
|
||||
// Function to bundle and copy the CSS file
|
||||
const bundleAndCopyFile = async (source, destination) => {
|
||||
@@ -26,10 +24,7 @@ const bundleAndCopyFile = async (source, destination) => {
|
||||
};
|
||||
|
||||
// Initial bundle and copy
|
||||
Promise.all([
|
||||
bundleAndCopyFile(source1, destination1),
|
||||
bundleAndCopyFile(source2, destination2),
|
||||
])
|
||||
Promise.all([bundleAndCopyFile(source1, destination1)])
|
||||
.then(() => {
|
||||
// Add an empty line after all operations are complete - It looks better in the terminal :)
|
||||
console.log('');
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { RadioGroupSnippet } from '@/snippets/stories-snippets';
|
||||
import {
|
||||
radioGroupPropDefs,
|
||||
radioGroupUsageSnippet,
|
||||
radioGroupDefaultSnippet,
|
||||
radioGroupDescriptionSnippet,
|
||||
radioGroupHorizontalSnippet,
|
||||
radioGroupDisabledSnippet,
|
||||
radioGroupDisabledSingleSnippet,
|
||||
radioGroupValidationSnippet,
|
||||
radioGroupReadOnlySnippet,
|
||||
} from './radio-group.props';
|
||||
import { ComponentInfos } from '@/components/ComponentInfos';
|
||||
|
||||
# RadioGroup
|
||||
|
||||
A radio group allows a user to select a single item from a list of mutually exclusive options.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
preview={<RadioGroupSnippet story="Default" />}
|
||||
code={radioGroupDefaultSnippet}
|
||||
/>
|
||||
|
||||
<ComponentInfos
|
||||
component="radio-group"
|
||||
classNames={['canon-RadioGroup', 'canon-RadioGroupContent']}
|
||||
usageCode={radioGroupUsageSnippet}
|
||||
/>
|
||||
|
||||
## API reference
|
||||
|
||||
<PropsTable data={radioGroupPropDefs} />
|
||||
|
||||
## Examples
|
||||
|
||||
### Horizontal
|
||||
|
||||
Here's a simple TextField with a description.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<RadioGroupSnippet story="Horizontal" />}
|
||||
code={radioGroupHorizontalSnippet}
|
||||
/>
|
||||
|
||||
### Disabled
|
||||
|
||||
You can disable the entire radio group by adding the `isDisabled` prop to the `RadioGroup` component.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<RadioGroupSnippet story="Disabled" />}
|
||||
code={radioGroupDisabledSnippet}
|
||||
/>
|
||||
|
||||
### Disabled Single radio
|
||||
|
||||
You can disable a single radio by adding the `isDisabled` prop to the `Radio` component.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<RadioGroupSnippet story="DisabledSingle" />}
|
||||
code={radioGroupDisabledSingleSnippet}
|
||||
/>
|
||||
|
||||
### Validation
|
||||
|
||||
Here's an example of a radio group with errors.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<RadioGroupSnippet story="Validation" />}
|
||||
code={radioGroupValidationSnippet}
|
||||
/>
|
||||
|
||||
### Read only
|
||||
|
||||
You can make the radio group read only by adding the `isReadOnly` prop to the `RadioGroup` component.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<RadioGroupSnippet story="ReadOnly" />}
|
||||
code={radioGroupReadOnlySnippet}
|
||||
/>
|
||||
@@ -0,0 +1,64 @@
|
||||
import {
|
||||
classNamePropDefs,
|
||||
stylePropDefs,
|
||||
type PropDef,
|
||||
} from '@/utils/propDefs';
|
||||
|
||||
export const radioGroupPropDefs: Record<string, PropDef> = {
|
||||
size: {
|
||||
type: 'enum',
|
||||
values: ['small', 'medium'],
|
||||
default: 'small',
|
||||
responsive: true,
|
||||
},
|
||||
label: {
|
||||
type: 'string',
|
||||
},
|
||||
icon: {
|
||||
type: 'enum',
|
||||
values: ['ReactNode'],
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const radioGroupUsageSnippet = `import { RadioGroup } from '@backstage/canon';
|
||||
|
||||
<RadioGroup />`;
|
||||
|
||||
export const radioGroupDefaultSnippet = `<RadioGroup label="Label" />`;
|
||||
|
||||
export const radioGroupDescriptionSnippet = `<TextField label="Label" description="Description" placeholder="Enter a URL" />`;
|
||||
|
||||
export const radioGroupHorizontalSnippet = `<RadioGroup label="Label" orientation="horizontal" />`;
|
||||
|
||||
export const radioGroupDisabledSnippet = `<RadioGroup label="Label" isDisabled>
|
||||
<Radio value="bulbasaur">Bulbasaur</Radio>
|
||||
<Radio value="charmander">Charmander</Radio>
|
||||
<Radio value="squirtle">Squirtle</Radio>
|
||||
</RadioGroup>`;
|
||||
|
||||
export const radioGroupDisabledSingleSnippet = `<RadioGroup label="Label">
|
||||
<Radio value="bulbasaur">Bulbasaur</Radio>
|
||||
<Radio value="charmander" isDisabled>Charmander</Radio>
|
||||
<Radio value="squirtle">Squirtle</Radio>
|
||||
</RadioGroup>`;
|
||||
|
||||
export const radioGroupValidationSnippet = `<RadioGroup validate: value => (value === \'charmander\' ? \'Nice try!\' : null)>
|
||||
<Radio value="bulbasaur">Bulbasaur</Radio>
|
||||
<Radio value="charmander">Charmander</Radio>
|
||||
<Radio value="squirtle">Squirtle</Radio>
|
||||
</RadioGroup>`;
|
||||
|
||||
export const radioGroupReadOnlySnippet = `<RadioGroup label="Label" isReadOnly>
|
||||
<Radio value="bulbasaur">Bulbasaur</Radio>
|
||||
<Radio value="charmander">Charmander</Radio>
|
||||
<Radio value="squirtle">Squirtle</Radio>
|
||||
</RadioGroup>`;
|
||||
@@ -18,6 +18,7 @@ import * as MenuStories from '../../../packages/canon/src/components/Menu/Menu.s
|
||||
import * as LinkStories from '../../../packages/canon/src/components/Link/Link.stories';
|
||||
import * as AvatarStories from '../../../packages/canon/src/components/Avatar/Avatar.stories';
|
||||
import * as CollapsibleStories from '../../../packages/canon/src/components/Collapsible/Collapsible.stories';
|
||||
import * as RadioGroupStories from '../../../packages/canon/src/components/RadioGroup/RadioGroup.stories';
|
||||
import * as TabsStories from '../../../packages/canon/src/components/Tabs/Tabs.stories';
|
||||
import * as SwitchStories from '../../../packages/canon/src/components/Switch/Switch.stories';
|
||||
|
||||
@@ -197,3 +198,14 @@ export const SwitchSnippet = ({
|
||||
|
||||
return StoryComponent ? <StoryComponent /> : null;
|
||||
};
|
||||
|
||||
export const RadioGroupSnippet = ({
|
||||
story,
|
||||
}: {
|
||||
story: keyof typeof RadioGroupStories;
|
||||
}) => {
|
||||
const stories = composeStories(RadioGroupStories);
|
||||
const StoryComponent = stories[story as keyof typeof stories];
|
||||
|
||||
return StoryComponent ? <StoryComponent /> : null;
|
||||
};
|
||||
|
||||
@@ -116,6 +116,11 @@ export const components: Page[] = [
|
||||
slug: 'menu',
|
||||
status: 'alpha',
|
||||
},
|
||||
{
|
||||
title: 'RadioGroup',
|
||||
slug: 'radio-group',
|
||||
status: 'alpha',
|
||||
},
|
||||
{
|
||||
title: 'Select',
|
||||
slug: 'select',
|
||||
|
||||
@@ -12,7 +12,7 @@ by writing custom actions which can be used alongside our
|
||||
|
||||
When adding custom actions, the actions array will **replace the
|
||||
built-in actions too**. Meaning, you will no longer be able to use them.
|
||||
If you want to continue using the builtin actions, include them in the actions
|
||||
If you want to continue using the builtin actions, include them in the `actions`
|
||||
array when registering your custom actions, as seen below.
|
||||
|
||||
:::
|
||||
@@ -52,19 +52,20 @@ its generated unit test. We will replace the existing placeholder code with our
|
||||
import { resolveSafeChildPath } from '@backstage/backend-plugin-api';
|
||||
import { createTemplateAction } from '@backstage/plugin-scaffolder-node';
|
||||
import fs from 'fs-extra';
|
||||
import { z } from 'zod';
|
||||
import { type z } from 'zod';
|
||||
|
||||
export const createNewFileAction = () => {
|
||||
return createTemplateAction({
|
||||
id: 'acme:file:create',
|
||||
description: 'Create an Acme file.',
|
||||
schema: {
|
||||
input: z.object({
|
||||
contents: z.string().describe('The contents of the file'),
|
||||
filename: z
|
||||
.string()
|
||||
.describe('The filename of the file that will be created'),
|
||||
}),
|
||||
input: {
|
||||
contents: z => z.string({ description: 'The contents of the file' }),
|
||||
filename: z =>
|
||||
z.string({
|
||||
description: 'The filename of the file that will be created',
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
||||
async handler(ctx) {
|
||||
@@ -95,53 +96,11 @@ The `createTemplateAction` takes an object which specifies the following:
|
||||
function using `ctx.output`
|
||||
- `handler` - the actual code which is run as part of the action, with a context
|
||||
|
||||
You can also choose to define your custom action using JSON schema instead of `zod`:
|
||||
|
||||
```ts title="With JSON Schema"
|
||||
import { resolveSafeChildPath } from '@backstage/backend-plugin-api';
|
||||
import { createTemplateAction } from '@backstage/plugin-scaffolder-node';
|
||||
import { writeFile } from 'fs';
|
||||
|
||||
export const createNewFileAction = () => {
|
||||
return createTemplateAction<{ contents: string; filename: string }>({
|
||||
id: 'acme:file:create',
|
||||
description: 'Create an Acme file.',
|
||||
schema: {
|
||||
input: {
|
||||
required: ['contents', 'filename'],
|
||||
type: 'object',
|
||||
properties: {
|
||||
contents: {
|
||||
type: 'string',
|
||||
title: 'Contents',
|
||||
description: 'The contents of the file',
|
||||
},
|
||||
filename: {
|
||||
type: 'string',
|
||||
title: 'Filename',
|
||||
description: 'The filename of the file that will be created',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
async handler(ctx) {
|
||||
const { signal } = ctx;
|
||||
await writeFile(
|
||||
resolveSafeChildPath(ctx.workspacePath, ctx.input.filename),
|
||||
ctx.input.contents,
|
||||
{ signal },
|
||||
_ => {},
|
||||
);
|
||||
},
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
### Naming Conventions
|
||||
|
||||
Try to keep names consistent for both your own custom actions, and any actions contributed to open source. We've found
|
||||
that a separation of `:` and using a verb as the last part of the name works well.
|
||||
We follow `provider:entity:verb` or as close to this as possible for our built in actions. For example,
|
||||
We follow `provider:entity:verb` or as close to this as possible for our built-in actions. For example,
|
||||
`github:actions:create` or `github:repo:create`.
|
||||
|
||||
Also feel free to use your company name to namespace them if you prefer too, for example `acme:file:create` like above.
|
||||
@@ -151,14 +110,14 @@ and writing of template entity definitions.
|
||||
|
||||
### Adding a TemplateExample
|
||||
|
||||
A TemplateExample is a way to document different ways that your custom action can be used. Once added it will be visible
|
||||
A TemplateExample is a way to document different ways that your custom action can be used. Once added, it will be visible
|
||||
in your Backstage instance under the [/create/actions](https://demo.backstage.io/create/actions) path. You can have multiple
|
||||
examples for one action that can demonstrate different combinations of inputs and how to use them.
|
||||
|
||||
#### Define TemplateExamples
|
||||
|
||||
Below is a sample TemplateExample that is used for `publish:github`. The source code is available
|
||||
on [github](https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend-module-github/src/actions/github.examples.ts)
|
||||
on [GitHub](https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend-module-github/src/actions/github.examples.ts)
|
||||
and preview on [demo.backstage.io/create/actions](https://demo.backstage.io/create/actions#publish-github)
|
||||
|
||||
```ts title="With JSON Schema"
|
||||
@@ -222,7 +181,7 @@ return createTemplateAction({
|
||||
#### Test TemplateAction examples
|
||||
|
||||
It is also possible to test your example TemplateActions. You can see a sample test
|
||||
on [github](https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend-module-github/src/actions/github.examples.test.ts)
|
||||
on [GitHub](https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend-module-github/src/actions/github.examples.test.ts)
|
||||
|
||||
### The context object
|
||||
|
||||
@@ -234,13 +193,12 @@ argument. It looks like the following:
|
||||
implement [idempotency of the actions](https://github.com/backstage/backstage/tree/master/beps/0004-scaffolder-task-idempotency)
|
||||
by not re-running the same function again if it was
|
||||
executed successfully on the previous run.
|
||||
- `ctx.logger` - a Winston logger for additional logging inside your action
|
||||
- `ctx.logStream` - a stream version of the logger if needed
|
||||
- `ctx.logger` - a [LoggerService](../../backend-system/core-services/logger.md) instance for additional logging inside your action
|
||||
- `ctx.workspacePath` - a string of the working directory of the template run
|
||||
- `ctx.input` - an object which should match the `zod` or JSON schema provided in the
|
||||
`schema.input` part of the action definition
|
||||
- `ctx.output` - a function which you can call to set outputs that match the
|
||||
JSON schema or `zod` in `schema.output` for ex. `ctx.output('downloadUrl', myDownloadUrl)`
|
||||
`zod` schema in `schema.output` for ex. `ctx.output('downloadUrl', myDownloadUrl)`
|
||||
- `createTemporaryDirectory` a function to call to give you a temporary
|
||||
directory somewhere on the runner, so you can store some files there rather
|
||||
than polluting the `workspacePath`
|
||||
@@ -249,7 +207,7 @@ argument. It looks like the following:
|
||||
|
||||
## Registering Custom Actions
|
||||
|
||||
To register your new custom action in the Backend System you will need to create a backend module. Here is a very
|
||||
To register your new custom action in the Backend System, you will need to create a backend module. Here is a very
|
||||
simplified example of how to do that:
|
||||
|
||||
```ts title="packages/backend/src/index.ts"
|
||||
@@ -327,8 +285,8 @@ const res = await ctx.checkpoint?.({
|
||||
});
|
||||
```
|
||||
|
||||
You have to define the unique key in scope of the scaffolder task for your checkpoint. During the execution task engine
|
||||
will check if the checkpoint with such key was already executed or not, if yes, and the run was successful, the callback
|
||||
You have to define the unique key in the scope of the scaffolder task for your checkpoint. During the execution task engine
|
||||
will check if the checkpoint with such a key was already executed or not, if yes, and the run was successful, the callback
|
||||
will be skipped and instead the stored value will be returned.
|
||||
|
||||
Whenever you change the return type of the checkpoint, we encourage you to change the ID.
|
||||
|
||||
+2
-1
@@ -108,7 +108,8 @@
|
||||
"csstype@npm:^3.0.2": "3.0.9",
|
||||
"csstype@npm:^3.1.2": "3.0.9",
|
||||
"csstype@npm:^3.1.3": "3.0.9",
|
||||
"jest-haste-map@^29.7.0": "patch:jest-haste-map@npm%3A29.7.0#./.yarn/patches/jest-haste-map-npm-29.7.0-e3be419eff.patch"
|
||||
"jest-haste-map@^29.7.0": "patch:jest-haste-map@npm%3A29.7.0#./.yarn/patches/jest-haste-map-npm-29.7.0-e3be419eff.patch",
|
||||
"GendocuPublicApis": "npm:gendocu-public-apis@^1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/errors": "workspace:^",
|
||||
|
||||
@@ -42,6 +42,23 @@ describe('credentials', () => {
|
||||
},
|
||||
});
|
||||
|
||||
expect(
|
||||
createCredentialsWithUserPrincipal(
|
||||
'user:default/mock',
|
||||
'my-token',
|
||||
undefined,
|
||||
'my-actor',
|
||||
),
|
||||
).toEqual({
|
||||
$$type: '@backstage/BackstageCredentials',
|
||||
version: 'v1',
|
||||
principal: {
|
||||
type: 'user',
|
||||
userEntityRef: 'user:default/mock',
|
||||
actor: { type: 'service', subject: 'my-actor' },
|
||||
},
|
||||
});
|
||||
|
||||
expect(createCredentialsWithNonePrincipal()).toEqual({
|
||||
$$type: '@backstage/BackstageCredentials',
|
||||
version: 'v1',
|
||||
@@ -64,4 +81,63 @@ describe('credentials', () => {
|
||||
),
|
||||
).not.toMatch(/my-token/);
|
||||
});
|
||||
|
||||
it('should have a serializable form both as strings and as JSON', () => {
|
||||
const simpleService = createCredentialsWithServicePrincipal('my-service');
|
||||
expect(String(simpleService)).toMatchInlineSnapshot(
|
||||
`"backstageCredentials{servicePrincipal{my-service}}"`,
|
||||
);
|
||||
expect(JSON.stringify(simpleService)).toMatchInlineSnapshot(
|
||||
`"{"$$type":"@backstage/BackstageCredentials","version":"v1","principal":{"type":"service","subject":"my-service"}}"`,
|
||||
);
|
||||
|
||||
const serviceWithAccessRestrictions = createCredentialsWithServicePrincipal(
|
||||
'my-service',
|
||||
undefined,
|
||||
{
|
||||
permissionNames: ['perm'],
|
||||
permissionAttributes: {
|
||||
action: ['read'],
|
||||
},
|
||||
},
|
||||
);
|
||||
expect(String(serviceWithAccessRestrictions)).toMatchInlineSnapshot(
|
||||
`"backstageCredentials{servicePrincipal{my-service,accessRestrictions=cXWOJgUirHkHNZIowUi/YO5nwEwhTicC38iXi2XTYCk}}"`,
|
||||
);
|
||||
expect(JSON.stringify(serviceWithAccessRestrictions)).toMatchInlineSnapshot(
|
||||
`"{"$$type":"@backstage/BackstageCredentials","version":"v1","principal":{"type":"service","subject":"my-service","accessRestrictions":{"permissionNames":["perm"],"permissionAttributes":{"action":["read"]}}}}"`,
|
||||
);
|
||||
|
||||
const simpleUser = createCredentialsWithUserPrincipal(
|
||||
'user:default/mock',
|
||||
'my-token',
|
||||
);
|
||||
expect(String(simpleUser)).toMatchInlineSnapshot(
|
||||
`"backstageCredentials{userPrincipal{user:default/mock}}"`,
|
||||
);
|
||||
expect(JSON.stringify(simpleUser)).toMatchInlineSnapshot(
|
||||
`"{"$$type":"@backstage/BackstageCredentials","version":"v1","principal":{"type":"user","userEntityRef":"user:default/mock"}}"`,
|
||||
);
|
||||
|
||||
const userWithActor = createCredentialsWithUserPrincipal(
|
||||
'user:default/mock',
|
||||
'my-token',
|
||||
undefined,
|
||||
'my-actor',
|
||||
);
|
||||
expect(String(userWithActor)).toMatchInlineSnapshot(
|
||||
`"backstageCredentials{userPrincipal{user:default/mock,actor={servicePrincipal{my-actor}}}}"`,
|
||||
);
|
||||
expect(JSON.stringify(userWithActor)).toMatchInlineSnapshot(
|
||||
`"{"$$type":"@backstage/BackstageCredentials","version":"v1","principal":{"type":"user","userEntityRef":"user:default/mock","actor":{"type":"service","subject":"my-actor"}}}"`,
|
||||
);
|
||||
|
||||
const none = createCredentialsWithNonePrincipal();
|
||||
expect(String(none)).toMatchInlineSnapshot(
|
||||
`"backstageCredentials{nonePrincipal}"`,
|
||||
);
|
||||
expect(JSON.stringify(none)).toMatchInlineSnapshot(
|
||||
`"{"$$type":"@backstage/BackstageCredentials","version":"v1","principal":{"type":"none"}}"`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,29 +22,34 @@ import {
|
||||
BackstageUserPrincipal,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { InternalBackstageCredentials } from './types';
|
||||
import { createHash } from 'crypto';
|
||||
|
||||
export function createCredentialsWithServicePrincipal(
|
||||
sub: string,
|
||||
token?: string,
|
||||
accessRestrictions?: BackstagePrincipalAccessRestrictions,
|
||||
): InternalBackstageCredentials<BackstageServicePrincipal> {
|
||||
return Object.defineProperty(
|
||||
{
|
||||
$$type: '@backstage/BackstageCredentials',
|
||||
version: 'v1',
|
||||
principal: {
|
||||
type: 'service',
|
||||
subject: sub,
|
||||
accessRestrictions,
|
||||
},
|
||||
},
|
||||
'token',
|
||||
{
|
||||
const principal = createServicePrincipal(sub, accessRestrictions);
|
||||
const result = {
|
||||
$$type: '@backstage/BackstageCredentials',
|
||||
version: 'v1',
|
||||
principal,
|
||||
} as const;
|
||||
Object.defineProperties(result, {
|
||||
token: {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: token,
|
||||
},
|
||||
);
|
||||
toString: {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: () => `backstageCredentials{${principal}}`,
|
||||
},
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
export function createCredentialsWithUserPrincipal(
|
||||
@@ -53,36 +58,49 @@ export function createCredentialsWithUserPrincipal(
|
||||
expiresAt?: Date,
|
||||
actor?: string,
|
||||
): InternalBackstageCredentials<BackstageUserPrincipal> {
|
||||
return Object.defineProperty(
|
||||
{
|
||||
$$type: '@backstage/BackstageCredentials',
|
||||
version: 'v1',
|
||||
expiresAt,
|
||||
principal: {
|
||||
type: 'user',
|
||||
userEntityRef: sub,
|
||||
...(actor && {
|
||||
actor: { type: 'service', subject: actor },
|
||||
}),
|
||||
},
|
||||
},
|
||||
'token',
|
||||
{
|
||||
const principal = createUserPrincipal(
|
||||
sub,
|
||||
actor ? createServicePrincipal(actor) : undefined,
|
||||
);
|
||||
const result = {
|
||||
$$type: '@backstage/BackstageCredentials',
|
||||
version: 'v1',
|
||||
expiresAt,
|
||||
principal,
|
||||
} as const;
|
||||
Object.defineProperties(result, {
|
||||
token: {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: token,
|
||||
},
|
||||
);
|
||||
toString: {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: () => `backstageCredentials{${principal}}`,
|
||||
},
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
export function createCredentialsWithNonePrincipal(): InternalBackstageCredentials<BackstageNonePrincipal> {
|
||||
return {
|
||||
const principal = createNonePrincipal();
|
||||
const result = {
|
||||
$$type: '@backstage/BackstageCredentials',
|
||||
version: 'v1',
|
||||
principal: {
|
||||
type: 'none',
|
||||
principal,
|
||||
} as const;
|
||||
Object.defineProperties(result, {
|
||||
toString: {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: () => `backstageCredentials{${principal}}`,
|
||||
},
|
||||
};
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
export function toInternalBackstageCredentials(
|
||||
@@ -106,3 +124,74 @@ export function toInternalBackstageCredentials(
|
||||
|
||||
return internalCredentials;
|
||||
}
|
||||
|
||||
function createServicePrincipal(
|
||||
sub: string,
|
||||
accessRestrictions?: BackstagePrincipalAccessRestrictions,
|
||||
): BackstageServicePrincipal {
|
||||
const result = {
|
||||
type: 'service',
|
||||
subject: sub,
|
||||
accessRestrictions,
|
||||
} as const;
|
||||
Object.defineProperties(result, {
|
||||
toString: {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: () => {
|
||||
let parts = sub;
|
||||
if (accessRestrictions) {
|
||||
const hash = createHash('sha256')
|
||||
.update(JSON.stringify(accessRestrictions))
|
||||
.digest('base64')
|
||||
.replace(/=+$/, '');
|
||||
parts += `,accessRestrictions=${hash}`;
|
||||
}
|
||||
return `servicePrincipal{${parts}}`;
|
||||
},
|
||||
},
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function createUserPrincipal(
|
||||
userEntityRef: string,
|
||||
actor?: BackstageServicePrincipal,
|
||||
): BackstageUserPrincipal {
|
||||
const result = {
|
||||
type: 'user',
|
||||
userEntityRef,
|
||||
actor,
|
||||
} as const;
|
||||
Object.defineProperties(result, {
|
||||
toString: {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: () => {
|
||||
let parts = userEntityRef;
|
||||
if (actor) {
|
||||
parts += `,actor={${actor}}`;
|
||||
}
|
||||
return `userPrincipal{${parts}}`;
|
||||
},
|
||||
},
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function createNonePrincipal(): BackstageNonePrincipal {
|
||||
const result = {
|
||||
type: 'none',
|
||||
} as const;
|
||||
Object.defineProperties(result, {
|
||||
toString: {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: () => 'nonePrincipal',
|
||||
},
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -170,4 +170,27 @@ describe('mockCredentials', () => {
|
||||
"Invalid user entity reference 'wrong', expected <kind>:<namespace>/<name>",
|
||||
);
|
||||
});
|
||||
|
||||
it('should have a serializable form', () => {
|
||||
expect(String(mockCredentials.service('my-service'))).toMatchInlineSnapshot(
|
||||
`"mockCredentials{servicePrincipal{my-service}}"`,
|
||||
);
|
||||
expect(
|
||||
String(mockCredentials.user('user:default/mock')),
|
||||
).toMatchInlineSnapshot(
|
||||
`"mockCredentials{userPrincipal{user:default/mock}}"`,
|
||||
);
|
||||
expect(
|
||||
String(
|
||||
mockCredentials.user('user:default/mock', {
|
||||
actor: { subject: 'my-actor' },
|
||||
}),
|
||||
),
|
||||
).toMatchInlineSnapshot(
|
||||
`"mockCredentials{userPrincipal{user:default/mock,actor={my-actor}}}"`,
|
||||
);
|
||||
expect(String(mockCredentials.none())).toMatchInlineSnapshot(
|
||||
`"mockCredentials{nonePrincipal}"`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -76,10 +76,19 @@ export namespace mockCredentials {
|
||||
* Creates a mocked credentials object for a unauthenticated principal.
|
||||
*/
|
||||
export function none(): BackstageCredentials<BackstageNonePrincipal> {
|
||||
return {
|
||||
const result = {
|
||||
$$type: '@backstage/BackstageCredentials',
|
||||
principal: { type: 'none' },
|
||||
};
|
||||
} as const;
|
||||
Object.defineProperties(result, {
|
||||
toString: {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: () => `mockCredentials{nonePrincipal}`,
|
||||
},
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,24 +120,32 @@ export namespace mockCredentials {
|
||||
options?: { actor?: { subject: string } },
|
||||
): BackstageCredentials<BackstageUserPrincipal> {
|
||||
validateUserEntityRef(userEntityRef);
|
||||
return Object.defineProperty(
|
||||
{
|
||||
$$type: '@backstage/BackstageCredentials',
|
||||
principal: {
|
||||
type: 'user',
|
||||
userEntityRef,
|
||||
...(options?.actor && {
|
||||
actor: { type: 'service', subject: options.actor.subject },
|
||||
}),
|
||||
},
|
||||
const result = {
|
||||
$$type: '@backstage/BackstageCredentials',
|
||||
principal: {
|
||||
type: 'user',
|
||||
userEntityRef,
|
||||
...(options?.actor && {
|
||||
actor: { type: 'service', subject: options.actor.subject } as const,
|
||||
}),
|
||||
},
|
||||
'token',
|
||||
{
|
||||
} as const;
|
||||
Object.defineProperties(result, {
|
||||
toString: {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: () =>
|
||||
`mockCredentials{userPrincipal{${userEntityRef}${
|
||||
options?.actor ? `,actor={${options.actor.subject}}` : ''
|
||||
}}}`,
|
||||
},
|
||||
token: {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: user.token(),
|
||||
},
|
||||
);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -231,14 +248,27 @@ export namespace mockCredentials {
|
||||
subject: string = DEFAULT_MOCK_SERVICE_SUBJECT,
|
||||
accessRestrictions?: BackstagePrincipalAccessRestrictions,
|
||||
): BackstageCredentials<BackstageServicePrincipal> {
|
||||
return {
|
||||
const result = {
|
||||
$$type: '@backstage/BackstageCredentials',
|
||||
principal: {
|
||||
type: 'service',
|
||||
subject,
|
||||
...(accessRestrictions ? { accessRestrictions } : {}),
|
||||
},
|
||||
};
|
||||
} as const;
|
||||
Object.defineProperties(result, {
|
||||
toString: {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: () =>
|
||||
`mockCredentials{servicePrincipal{${subject}${
|
||||
accessRestrictions
|
||||
? `,accessRestrictions=${JSON.stringify(accessRestrictions)}`
|
||||
: ''
|
||||
}}}`,
|
||||
},
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
.canon-AvatarRoot {
|
||||
vertical-align: middle;
|
||||
user-select: none;
|
||||
color: var(--canon-fg-primary);
|
||||
background-color: var(--canon-bg-surface-2);
|
||||
border-radius: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
line-height: 1;
|
||||
display: inline-flex;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.canon-AvatarRoot[data-size="small"] {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
}
|
||||
|
||||
.canon-AvatarRoot[data-size="medium"] {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
.canon-AvatarRoot[data-size="large"] {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
}
|
||||
|
||||
.canon-AvatarImage {
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.canon-AvatarFallback {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: var(--canon-font-size-3);
|
||||
font-weight: var(--canon-font-weight-regular);
|
||||
box-shadow: inset 0 0 0 1px var(--canon-border);
|
||||
border-radius: var(--canon-radius-full);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
.canon-Box {
|
||||
font-family: var(--canon-font-regular);
|
||||
font-weight: var(--canon-font-weight-regular);
|
||||
color: var(--canon-fg-primary);
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
.canon-Button {
|
||||
user-select: none;
|
||||
font-family: var(--canon-font-regular);
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
cursor: pointer;
|
||||
border-radius: var(--canon-radius-2);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: var(--canon-space-1_5);
|
||||
border: none;
|
||||
padding: 0;
|
||||
display: inline-flex;
|
||||
|
||||
&:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
.canon-Button[data-variant="primary"] {
|
||||
background-color: var(--canon-bg-solid);
|
||||
color: var(--canon-fg-solid);
|
||||
transition: background-color .15s, box-shadow .15s;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--canon-bg-solid-hover);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: var(--canon-bg-solid-pressed);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: 2px solid var(--canon-ring);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background-color: var(--canon-bg-solid-disabled);
|
||||
color: var(--canon-fg-solid-disabled);
|
||||
}
|
||||
}
|
||||
|
||||
.canon-Button[data-variant="secondary"] {
|
||||
background-color: var(--canon-bg-surface-1);
|
||||
box-shadow: inset 0 0 0 1px var(--canon-border);
|
||||
color: var(--canon-fg-primary);
|
||||
transition: box-shadow .15s;
|
||||
|
||||
&:hover {
|
||||
box-shadow: inset 0 0 0 1px var(--canon-border-hover);
|
||||
}
|
||||
|
||||
&:active {
|
||||
box-shadow: inset 0 0 0 1px var(--canon-border-pressed);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
box-shadow: inset 0 0 0 2px var(--canon-ring);
|
||||
outline: none;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
box-shadow: inset 0 0 0 1px var(--canon-border-disabled);
|
||||
color: var(--canon-fg-disabled);
|
||||
}
|
||||
}
|
||||
|
||||
.canon-Button[data-size="medium"] {
|
||||
font-size: var(--canon-font-size-4);
|
||||
padding: 0 var(--canon-space-3);
|
||||
height: 2.5rem;
|
||||
}
|
||||
|
||||
.canon-Button[data-size="small"] {
|
||||
font-size: var(--canon-font-size-3);
|
||||
padding: 0 var(--canon-space-2);
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
.canon-Button[data-size="small"] svg {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
|
||||
.canon-Button[data-size="medium"] svg {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
.canon-ButtonIcon {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.canon-ButtonIcon[data-size="small"] {
|
||||
width: 2rem;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.canon-ButtonIcon[data-size="medium"] {
|
||||
width: 2.5rem;
|
||||
padding: 0;
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
.canon-CheckboxRoot {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
box-shadow: inset 0 0 0 1px var(--canon-border);
|
||||
cursor: pointer;
|
||||
background-color: var(--canon-bg-surface-1);
|
||||
border: none;
|
||||
border-radius: 2px;
|
||||
flex-shrink: 0;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0;
|
||||
transition: background-color .2s ease-in-out;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.canon-CheckboxRoot:focus-visible {
|
||||
outline: 2px solid var(--canon-ring);
|
||||
outline-offset: 2px;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.canon-CheckboxRoot[data-checked] {
|
||||
background-color: var(--canon-bg-solid);
|
||||
box-shadow: none;
|
||||
color: var(--canon-fg-solid);
|
||||
}
|
||||
|
||||
.canon-CheckboxLabel {
|
||||
align-items: center;
|
||||
gap: var(--canon-space-2);
|
||||
font-size: var(--canon-font-size-3);
|
||||
font-family: var(--canon-font-regular);
|
||||
font-weight: var(--canon-font-weight-regular);
|
||||
color: var(--canon-fg-primary);
|
||||
user-select: none;
|
||||
flex-direction: row;
|
||||
display: flex;
|
||||
|
||||
&:hover {
|
||||
& .canon-CheckboxRoot:not([data-checked]) {
|
||||
box-shadow: inset 0 0 0 1px var(--canon-border-hover);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.canon-CheckboxIndicator {
|
||||
color: var(--canon-fg-solid);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
.canon-CollapsiblePanel {
|
||||
height: var(--collapsible-panel-height);
|
||||
transition: all .15s ease-out;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
|
||||
&[data-starting-style], &[data-ending-style] {
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,18 +0,0 @@
|
||||
.canon-Container {
|
||||
max-width: 120rem;
|
||||
padding-inline: var(--canon-space-4);
|
||||
margin-inline: auto;
|
||||
transition: padding .2s ease-in-out;
|
||||
}
|
||||
|
||||
@media (width >= 640px) {
|
||||
.canon-Container {
|
||||
padding-inline: var(--canon-space-8);
|
||||
}
|
||||
}
|
||||
|
||||
@media (width >= 1024px) {
|
||||
.canon-Container {
|
||||
padding-inline: var(--canon-space-12);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,23 +0,0 @@
|
||||
.canon-DataTablePagination {
|
||||
padding-top: var(--canon-space-5);
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.canon-DataTablePagination--left {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.canon-DataTablePagination--right {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: var(--canon-space-2);
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.canon-DataTablePagination--select {
|
||||
min-width: 10.5rem;
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
.canon-FieldLabel {
|
||||
margin-bottom: var(--canon-space-3);
|
||||
gap: var(--canon-space-1);
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.canon-FieldLabelLabel {
|
||||
color: var(--canon-fg-primary);
|
||||
cursor: pointer;
|
||||
font-weight: var(--canon-font-weight-regular);
|
||||
font-size: var(--canon-font-size-2);
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.canon-FieldLabelSecondaryLabel {
|
||||
color: var(--canon-fg-secondary);
|
||||
font-weight: var(--canon-font-weight-regular);
|
||||
margin-left: var(--canon-space-1);
|
||||
}
|
||||
|
||||
.canon-FieldLabelDescription {
|
||||
font-weight: var(--canon-font-weight-regular);
|
||||
font-size: var(--canon-font-size-2);
|
||||
color: var(--canon-fg-secondary);
|
||||
margin: 0;
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
.canon-Flex {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
.canon-Grid {
|
||||
display: grid;
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
.canon-Heading {
|
||||
font-family: var(--canon-font-regular);
|
||||
color: var(--canon-fg-primary);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
line-height: 100%;
|
||||
}
|
||||
|
||||
.canon-Heading[data-variant="display"] {
|
||||
font-size: var(--canon-font-size-10);
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
}
|
||||
|
||||
.canon-Heading[data-variant="title1"] {
|
||||
font-size: var(--canon-font-size-9);
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
}
|
||||
|
||||
.canon-Heading[data-variant="title2"] {
|
||||
font-size: var(--canon-font-size-8);
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
}
|
||||
|
||||
.canon-Heading[data-variant="title3"] {
|
||||
font-size: var(--canon-font-size-7);
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
}
|
||||
|
||||
.canon-Heading[data-variant="title4"] {
|
||||
font-size: var(--canon-font-size-6);
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
}
|
||||
|
||||
.canon-Heading[data-variant="title5"] {
|
||||
font-size: var(--canon-font-size-5);
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
}
|
||||
|
||||
.canon-Heading[data-color="primary"] {
|
||||
color: var(--canon-fg-primary);
|
||||
}
|
||||
|
||||
.canon-Heading[data-color="secondary"] {
|
||||
color: var(--canon-fg-secondary);
|
||||
}
|
||||
|
||||
.canon-Heading[data-truncate] {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
.canon-Icon {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
.canon-IconButton {
|
||||
user-select: none;
|
||||
font-family: var(--canon-font-regular);
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
cursor: pointer;
|
||||
border-radius: var(--canon-radius-2);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: var(--canon-space-1_5);
|
||||
border: none;
|
||||
padding: 0;
|
||||
display: inline-flex;
|
||||
|
||||
&:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
.canon-IconButton[data-variant="primary"] {
|
||||
background-color: var(--canon-bg-solid);
|
||||
color: var(--canon-fg-solid);
|
||||
transition: background-color .15s, box-shadow .15s;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--canon-bg-solid-hover);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: var(--canon-bg-solid-pressed);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: 2px solid var(--canon-ring);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background-color: var(--canon-bg-solid-disabled);
|
||||
color: var(--canon-fg-solid-disabled);
|
||||
}
|
||||
}
|
||||
|
||||
.canon-IconButton[data-variant="secondary"] {
|
||||
background-color: var(--canon-bg-surface-1);
|
||||
box-shadow: inset 0 0 0 1px var(--canon-border);
|
||||
color: var(--canon-fg-primary);
|
||||
transition: box-shadow .15s;
|
||||
|
||||
&:hover {
|
||||
box-shadow: inset 0 0 0 1px var(--canon-border-hover);
|
||||
}
|
||||
|
||||
&:active {
|
||||
box-shadow: inset 0 0 0 1px var(--canon-border-pressed);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
box-shadow: inset 0 0 0 2px var(--canon-ring);
|
||||
outline: none;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
box-shadow: inset 0 0 0 1px var(--canon-border-disabled);
|
||||
color: var(--canon-fg-disabled);
|
||||
}
|
||||
}
|
||||
|
||||
.canon-IconButton[data-size="medium"] {
|
||||
font-size: var(--canon-font-size-4);
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.canon-IconButton[data-size="small"] {
|
||||
font-size: var(--canon-font-size-3);
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.canon-IconButtonIcon[data-size="small"], .canon-IconButtonIcon[data-size="small"] svg {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
|
||||
.canon-IconButtonIcon[data-size="medium"], .canon-IconButtonIcon[data-size="medium"] svg {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
.canon-Link {
|
||||
font-family: var(--canon-font-regular);
|
||||
color: var(--canon-fg-link);
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
text-decoration-line: none;
|
||||
|
||||
&:hover {
|
||||
color: var(--canon-fg-link-hover);
|
||||
text-underline-offset: calc(.025em + 2px);
|
||||
text-decoration-line: underline;
|
||||
text-decoration-style: solid;
|
||||
text-decoration-thickness: min(2px, max(1px, .05em));
|
||||
text-decoration-color: color-mix(in srgb, var(--canon-fg-link-hover) 30%, transparent);
|
||||
}
|
||||
}
|
||||
|
||||
.canon-Link[data-variant="body"] {
|
||||
font-size: var(--canon-font-size-3);
|
||||
line-height: 140%;
|
||||
}
|
||||
|
||||
.canon-Link[data-variant="subtitle"] {
|
||||
font-size: var(--canon-font-size-4);
|
||||
line-height: 140%;
|
||||
}
|
||||
|
||||
.canon-Link[data-variant="caption"] {
|
||||
font-size: var(--canon-font-size-2);
|
||||
line-height: 140%;
|
||||
}
|
||||
|
||||
.canon-Link[data-variant="label"] {
|
||||
font-size: var(--canon-font-size-1);
|
||||
line-height: 140%;
|
||||
}
|
||||
|
||||
.canon-Link[data-weight="regular"] {
|
||||
font-weight: var(--canon-font-weight-regular);
|
||||
}
|
||||
|
||||
.canon-Link[data-weight="bold"] {
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
.canon-MenuPositioner {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.canon-MenuPopup {
|
||||
background-color: var(--canon-bg-surface-1);
|
||||
border: 1px solid var(--canon-border);
|
||||
color: var(--canon-fg-primary);
|
||||
transform-origin: var(--transform-origin);
|
||||
max-width: min(var(--available-width), 340px);
|
||||
max-height: min(var(--available-height), 500px);
|
||||
padding-bottom: var(--canon-space-1);
|
||||
border-radius: .375rem;
|
||||
outline: none;
|
||||
flex-direction: column;
|
||||
transition: transform .15s, opacity .15s;
|
||||
display: flex;
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
|
||||
&[data-starting-style], &[data-ending-style] {
|
||||
opacity: 0;
|
||||
transform: scale(.9);
|
||||
}
|
||||
}
|
||||
|
||||
.canon-MenuItem {
|
||||
user-select: none;
|
||||
align-items: center;
|
||||
gap: var(--canon-space-2);
|
||||
height: 32px;
|
||||
color: var(--canon-fg-primary);
|
||||
border-radius: var(--canon-radius-2);
|
||||
margin-inline: var(--canon-space-1);
|
||||
padding-inline: var(--canon-space-2);
|
||||
font-size: var(--canon-font-size-3);
|
||||
cursor: pointer;
|
||||
outline: 0;
|
||||
flex-shrink: 0;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
|
||||
&:first-child {
|
||||
margin-top: var(--canon-space-1);
|
||||
}
|
||||
|
||||
&[data-highlighted] {
|
||||
background-color: var(--canon-gray-3);
|
||||
}
|
||||
}
|
||||
|
||||
.canon-MenuSubmenuTrigger {
|
||||
user-select: none;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: var(--canon-space-2);
|
||||
height: 32px;
|
||||
color: var(--canon-fg-primary);
|
||||
border-radius: var(--canon-radius-2);
|
||||
margin-inline: var(--canon-space-1);
|
||||
padding-inline: var(--canon-space-2);
|
||||
font-size: var(--canon-font-size-3);
|
||||
cursor: pointer;
|
||||
outline: 0;
|
||||
flex-shrink: 0;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
|
||||
& .canon-Icon {
|
||||
color: var(--canon-fg-secondary);
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
margin-top: var(--canon-space-1);
|
||||
}
|
||||
|
||||
&[data-popup-open], &[data-highlighted] {
|
||||
background-color: var(--canon-gray-3);
|
||||
|
||||
& .canon-Icon {
|
||||
color: var(--canon-fg-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.canon-MenuSeparator {
|
||||
background-color: var(--color-gray-200);
|
||||
height: 1px;
|
||||
margin: .375rem 1rem;
|
||||
}
|
||||
|
||||
.canon-SubmenuComboboxSearch {
|
||||
padding-inline: var(--canon-space-3);
|
||||
border: none;
|
||||
border-bottom: 1px solid var(--canon-border);
|
||||
background-color: var(--canon-bg-surface-1);
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
color: var(--canon-fg-primary);
|
||||
line-height: 140%;
|
||||
font-size: var(--canon-font-size-3);
|
||||
z-index: 1;
|
||||
outline: none;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
|
||||
&::placeholder {
|
||||
color: var(--canon-fg-secondary);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: .6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
.canon-SubmenuComboboxItems {
|
||||
padding-top: var(--canon-space-2);
|
||||
outline: none;
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.canon-SubmenuComboboxNoResults {
|
||||
padding-inline: var(--canon-space-3);
|
||||
padding-top: var(--canon-space-2);
|
||||
padding-bottom: var(--canon-space-4);
|
||||
color: var(--canon-fg-secondary);
|
||||
font-size: var(--canon-font-size-3);
|
||||
}
|
||||
|
||||
.canon-SubmenuComboboxItem {
|
||||
user-select: none;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: var(--canon-space-2);
|
||||
height: 32px;
|
||||
color: var(--canon-fg-primary);
|
||||
border-radius: var(--canon-radius-2);
|
||||
margin-inline: var(--canon-space-1);
|
||||
padding-inline: var(--canon-space-2);
|
||||
font-size: var(--canon-font-size-3);
|
||||
cursor: pointer;
|
||||
outline: 0;
|
||||
flex-shrink: 0;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
|
||||
&[data-highlighted] {
|
||||
background-color: var(--canon-gray-3);
|
||||
}
|
||||
|
||||
&[data-disabled] {
|
||||
opacity: .5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
.canon-SubmenuComboboxItemCheckbox {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
color: var(--canon-fg-primary);
|
||||
border-radius: var(--canon-radius-2);
|
||||
border: 1px solid var(--canon-border);
|
||||
background: var(--canon-bg-surface-1);
|
||||
flex-shrink: 0;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.canon-SubmenuComboboxItemLabel {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
.canon-ScrollAreaRoot {
|
||||
box-sizing: border-box;
|
||||
width: 24rem;
|
||||
max-width: calc(100vw - 8rem);
|
||||
height: 8.5rem;
|
||||
}
|
||||
|
||||
.canon-ScrollAreaViewport {
|
||||
overscroll-behavior: contain;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.canon-ScrollAreaContent {
|
||||
padding-block: .75rem;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1.5rem;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.canon-ScrollAreaScrollbar {
|
||||
background-color: var(--canon-scrollbar);
|
||||
opacity: 0;
|
||||
border-radius: .375rem;
|
||||
justify-content: center;
|
||||
width: .25rem;
|
||||
margin: .5rem;
|
||||
transition: opacity .15s .3s;
|
||||
display: flex;
|
||||
|
||||
&[data-hovering], &[data-scrolling] {
|
||||
opacity: 1;
|
||||
transition-duration: 75ms;
|
||||
transition-delay: 0s;
|
||||
}
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
width: 1.25rem;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
|
||||
.canon-ScrollAreaThumb {
|
||||
border-radius: inherit;
|
||||
background-color: var(--canon-scrollbar-thumb);
|
||||
width: 100%;
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
.canon-Select {
|
||||
font-family: var(--canon-font-regular);
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.canon-SelectLabel {
|
||||
font-size: var(--canon-font-size-2);
|
||||
font-weight: var(--canon-font-weight-regular);
|
||||
color: var(--canon-fg-primary);
|
||||
margin-bottom: var(--canon-space-1_5);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.canon-SelectLabel[data-disabled] {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.canon-SelectDescription {
|
||||
font-size: var(--canon-font-size-2);
|
||||
font-weight: var(--canon-font-weight-regular);
|
||||
color: var(--canon-fg-secondary);
|
||||
padding-top: var(--canon-space-1_5);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.canon-SelectError {
|
||||
font-size: var(--canon-font-size-2);
|
||||
font-weight: var(--canon-font-weight-regular);
|
||||
color: var(--canon-fg-danger);
|
||||
padding-top: var(--canon-space-1_5);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.canon-SelectTrigger {
|
||||
box-sizing: border-box;
|
||||
border-radius: var(--canon-radius-3);
|
||||
border: 1px solid var(--canon-border);
|
||||
padding: 0 var(--canon-space-4);
|
||||
background-color: var(--canon-bg-surface-1);
|
||||
font-size: var(--canon-font-size-3);
|
||||
font-family: var(--canon-font-regular);
|
||||
font-weight: var(--canon-font-weight-regular);
|
||||
color: var(--canon-fg-primary);
|
||||
cursor: pointer;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: var(--canon-space-2);
|
||||
width: 100%;
|
||||
transition: border-color .2s ease-in-out, outline-color .2s ease-in-out;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.canon-SelectTrigger::placeholder {
|
||||
color: var(--canon-fg-secondary);
|
||||
}
|
||||
|
||||
.canon-SelectTrigger:hover {
|
||||
border-color: var(--canon-border-hover);
|
||||
}
|
||||
|
||||
.canon-SelectTrigger:focus-visible {
|
||||
border-color: var(--canon-border-pressed);
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.canon-SelectTrigger[data-invalid] {
|
||||
border-color: var(--canon-fg-danger);
|
||||
}
|
||||
|
||||
.canon-SelectTrigger[data-invalid]:hover, .canon-SelectTrigger[data-invalid]:focus-visible {
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
.canon-SelectTrigger[data-disabled] {
|
||||
cursor: not-allowed;
|
||||
border-color: var(--canon-border-disabled);
|
||||
color: var(--canon-fg-disabled);
|
||||
}
|
||||
|
||||
.canon-SelectTrigger[data-size="small"] {
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
.canon-SelectTrigger[data-size="medium"] {
|
||||
height: 3rem;
|
||||
}
|
||||
|
||||
.canon-SelectIcon {
|
||||
margin-left: var(--canon-space-5);
|
||||
transition: transform .2s;
|
||||
}
|
||||
|
||||
.canon-SelectTrigger[data-popup-open] .canon-SelectIcon {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.canon-SelectPopup {
|
||||
box-sizing: border-box;
|
||||
max-height: var(--available-height);
|
||||
background-color: var(--canon-bg-surface-1);
|
||||
border: 1px solid var(--canon-border);
|
||||
border-radius: var(--canon-radius-3);
|
||||
padding-block: var(--canon-space-1);
|
||||
z-index: 1;
|
||||
transform-origin: var(--transform-origin);
|
||||
outline: 0;
|
||||
transition: transform .15s, opacity .15s;
|
||||
overflow-y: auto;
|
||||
box-shadow: 0 4px 12px #0003;
|
||||
}
|
||||
|
||||
.canon-SelectPopup[data-starting-style], .canon-SelectPopup[data-ending-style] {
|
||||
opacity: 0;
|
||||
transform: scale(.9);
|
||||
}
|
||||
|
||||
.canon-SelectItem {
|
||||
width: var(--anchor-width);
|
||||
padding-block: var(--canon-space-2);
|
||||
padding-inline: var(--canon-space-4);
|
||||
color: var(--canon-fg-primary);
|
||||
border-radius: var(--canon-radius-3);
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
font-size: var(--canon-font-size-3);
|
||||
align-items: center;
|
||||
gap: var(--canon-space-2);
|
||||
outline: none;
|
||||
grid-template-columns: 1rem 1fr;
|
||||
grid-template-areas: "icon text";
|
||||
display: grid;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.canon-SelectItem[data-highlighted] {
|
||||
z-index: 0;
|
||||
color: var(--canon-fg-primary);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.canon-SelectItem[data-highlighted]:before {
|
||||
content: "";
|
||||
z-index: -1;
|
||||
background-color: var(--canon-bg-tint-hover);
|
||||
border-radius: .25rem;
|
||||
position: absolute;
|
||||
inset-block: 0;
|
||||
inset-inline: .25rem;
|
||||
}
|
||||
|
||||
.canon-SelectItem[data-disabled] {
|
||||
cursor: not-allowed;
|
||||
color: var(--canon-fg-disabled);
|
||||
}
|
||||
|
||||
.canon-SelectItemIndicator {
|
||||
grid-area: icon;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.canon-SelectItemText {
|
||||
flex: 1;
|
||||
grid-area: text;
|
||||
}
|
||||
|
||||
.canon-SelectRequired {
|
||||
color: var(--canon-fg-secondary);
|
||||
font-size: var(--canon-font-size-2);
|
||||
font-weight: var(--canon-font-weight-regular);
|
||||
margin-left: var(--canon-space-1);
|
||||
}
|
||||
|
||||
.canon-SelectIcon {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.canon-SelectValue {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -9496,6 +9496,13 @@
|
||||
min-width: 10.5rem;
|
||||
}
|
||||
|
||||
.canon-FieldError {
|
||||
color: var(--canon-fg-danger);
|
||||
font-size: var(--canon-font-size-2);
|
||||
font-weight: var(--canon-font-weight-regular);
|
||||
margin-top: var(--canon-space-2);
|
||||
}
|
||||
|
||||
.canon-FieldLabel {
|
||||
margin-bottom: var(--canon-space-3);
|
||||
gap: var(--canon-space-1);
|
||||
@@ -9815,6 +9822,96 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.canon-RadioGroup {
|
||||
color: var(--canon-fg-primary);
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.canon-RadioGroup[data-orientation="horizontal"] .canon-RadioGroupContent {
|
||||
gap: var(--canon-space-4);
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.canon-RadioGroupContent {
|
||||
gap: var(--canon-space-2);
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.canon-Radio {
|
||||
align-items: center;
|
||||
gap: var(--canon-space-2);
|
||||
font-size: var(--canon-font-size-2);
|
||||
color: var(--canon-fg-primary);
|
||||
forced-color-adjust: none;
|
||||
display: flex;
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
box-sizing: border-box;
|
||||
border: .125rem solid var(--canon-border);
|
||||
background: var(--canon-gray-1);
|
||||
border-radius: var(--canon-radius-full);
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
transition: all .2s;
|
||||
display: block;
|
||||
}
|
||||
|
||||
&[data-pressed]:before {
|
||||
border-color: var(--canon-border);
|
||||
}
|
||||
|
||||
&[data-selected] {
|
||||
&:before {
|
||||
border-color: var(--canon-bg-solid);
|
||||
border-width: .25rem;
|
||||
}
|
||||
|
||||
&[data-pressed]:before {
|
||||
border-color: var(--canon-bg-solid);
|
||||
}
|
||||
}
|
||||
|
||||
&[data-focus-visible]:before {
|
||||
outline: 2px solid var(--canon-ring);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
&[data-disabled] {
|
||||
cursor: not-allowed;
|
||||
color: var(--canon-fg-disabled);
|
||||
|
||||
&:before {
|
||||
border-color: var(--canon-border-disabled);
|
||||
background: var(--canon-bg-disabled);
|
||||
}
|
||||
|
||||
&[data-selected]:before {
|
||||
border-color: var(--canon-border-disabled);
|
||||
}
|
||||
}
|
||||
|
||||
&[data-invalid]:before, &[data-invalid][data-selected]:before {
|
||||
border-color: var(--canon-border-danger);
|
||||
}
|
||||
|
||||
&[data-disabled][data-invalid] {
|
||||
color: var(--canon-fg-disabled);
|
||||
|
||||
&:before {
|
||||
border-color: var(--canon-border-disabled);
|
||||
background: var(--canon-bg-disabled);
|
||||
}
|
||||
|
||||
&[data-selected]:before {
|
||||
border-color: var(--canon-border-disabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.canon-TableRoot {
|
||||
caption-side: bottom;
|
||||
border-collapse: collapse;
|
||||
@@ -10119,13 +10216,6 @@
|
||||
border: 1px solid var(--canon-border-disabled);
|
||||
}
|
||||
|
||||
.canon-TextFieldError {
|
||||
color: var(--canon-fg-danger);
|
||||
font-size: var(--canon-font-size-2);
|
||||
font-weight: var(--canon-font-weight-regular);
|
||||
margin-top: var(--canon-space-2);
|
||||
}
|
||||
|
||||
.canon-TooltipPopup {
|
||||
box-sizing: border-box;
|
||||
transform-origin: var(--transform-origin);
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
.canon-Switch {
|
||||
align-items: center;
|
||||
gap: var(--canon-space-3);
|
||||
font-size: var(--canon-font-size-3);
|
||||
color: var(--canon-fg-primary);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
position: relative;
|
||||
|
||||
&[data-pressed] .canon-SwitchIndicator {
|
||||
&:before {
|
||||
background: var(--canon-fg-solid);
|
||||
}
|
||||
}
|
||||
|
||||
&[data-selected] {
|
||||
& .canon-SwitchIndicator {
|
||||
background: var(--canon-bg-solid);
|
||||
|
||||
&:before {
|
||||
background: var(--canon-fg-solid);
|
||||
transform: translateX(100%);
|
||||
}
|
||||
}
|
||||
|
||||
&[data-pressed] {
|
||||
& .indicator {
|
||||
background: var(--canon-gray-3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&[data-focus-visible] .canon-SwitchIndicator {
|
||||
outline-offset: 2px;
|
||||
outline: 2px solid;
|
||||
}
|
||||
}
|
||||
|
||||
.canon-SwitchIndicator {
|
||||
background: var(--canon-gray-3);
|
||||
border: 2px;
|
||||
border-radius: 1.143rem;
|
||||
width: 2rem;
|
||||
height: 1.143rem;
|
||||
transition: all .2s;
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
background: var(--canon-fg-solid);
|
||||
border-radius: 16px;
|
||||
width: .857rem;
|
||||
height: .857rem;
|
||||
margin: .143rem;
|
||||
transition: all .2s;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
.canon-TableCell {
|
||||
padding: var(--canon-space-3);
|
||||
font-size: var(--canon-font-size-3);
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
.canon-TabsRoot {
|
||||
border: 1px solid var(--color-gray-200);
|
||||
border-radius: .375rem;
|
||||
}
|
||||
|
||||
.canon-TabsList {
|
||||
z-index: 0;
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.canon-TabsTab {
|
||||
appearance: none;
|
||||
color: var(--canon-fg-secondary);
|
||||
user-select: none;
|
||||
padding-inline: var(--canon-space-3);
|
||||
height: 2rem;
|
||||
font-family: inherit;
|
||||
font-size: .875rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.25rem;
|
||||
font-size: var(--canon-font-size-2);
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
padding-block: 0;
|
||||
transition: color .2s ease-in-out;
|
||||
display: flex;
|
||||
|
||||
&[data-selected] {
|
||||
color: var(--canon-fg-primary);
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
&:hover {
|
||||
color: var(--canon-fg-primary);
|
||||
}
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
outline: 1px solid var(--canon-ring);
|
||||
outline-offset: -1px;
|
||||
border-radius: .25rem;
|
||||
position: absolute;
|
||||
inset: .25rem 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.canon-TabsIndicator {
|
||||
z-index: -1;
|
||||
translate: var(--active-tab-left) -50%;
|
||||
width: var(--active-tab-width);
|
||||
background-color: var(--canon-bg-solid);
|
||||
height: 1px;
|
||||
transition-property: translate, width;
|
||||
transition-duration: .2s;
|
||||
transition-timing-function: ease-in-out;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.canon-TabsPanel {
|
||||
&[hidden] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
.canon-Text {
|
||||
font-family: var(--canon-font-regular);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.canon-Text[data-variant="body"] {
|
||||
font-size: var(--canon-font-size-3);
|
||||
line-height: 140%;
|
||||
}
|
||||
|
||||
.canon-Text[data-variant="subtitle"] {
|
||||
font-size: var(--canon-font-size-4);
|
||||
line-height: 140%;
|
||||
}
|
||||
|
||||
.canon-Text[data-variant="caption"] {
|
||||
font-size: var(--canon-font-size-2);
|
||||
line-height: 140%;
|
||||
}
|
||||
|
||||
.canon-Text[data-variant="label"] {
|
||||
font-size: var(--canon-font-size-1);
|
||||
line-height: 140%;
|
||||
}
|
||||
|
||||
.canon-Text[data-weight="regular"] {
|
||||
font-weight: var(--canon-font-weight-regular);
|
||||
}
|
||||
|
||||
.canon-Text[data-weight="bold"] {
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
}
|
||||
|
||||
.canon-Text[data-color="primary"] {
|
||||
color: var(--canon-fg-primary);
|
||||
}
|
||||
|
||||
.canon-Text[data-color="secondary"] {
|
||||
color: var(--canon-fg-secondary);
|
||||
}
|
||||
|
||||
.canon-Text[data-color="danger"] {
|
||||
color: var(--canon-fg-danger);
|
||||
}
|
||||
|
||||
.canon-Text[data-color="warning"] {
|
||||
color: var(--canon-fg-warning);
|
||||
}
|
||||
|
||||
.canon-Text[data-color="success"] {
|
||||
color: var(--canon-fg-success);
|
||||
}
|
||||
|
||||
.canon-Text[data-truncate] {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
.canon-TextField {
|
||||
font-family: var(--canon-font-regular);
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.canon-TextFieldInputWrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.canon-TextFieldInputWrapper[data-size="small"] {
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
.canon-TextFieldInputWrapper[data-size="medium"] {
|
||||
height: 2.5rem;
|
||||
}
|
||||
|
||||
.canon-TextFieldIcon {
|
||||
left: var(--canon-space-3);
|
||||
margin-right: var(--canon-space-1);
|
||||
color: var(--canon-fg-primary);
|
||||
flex-shrink: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.canon-TextFieldIcon[data-size="small"], .canon-TextFieldIcon[data-size="small"] svg {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
|
||||
.canon-TextFieldIcon[data-size="medium"], .canon-TextFieldIcon[data-size="medium"] svg {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
|
||||
.canon-TextFieldInput {
|
||||
padding: 0 var(--canon-space-3);
|
||||
border-radius: var(--canon-radius-3);
|
||||
border: 1px solid var(--canon-border);
|
||||
background-color: var(--canon-bg-surface-1);
|
||||
font-size: var(--canon-font-size-3);
|
||||
font-family: var(--canon-font-regular);
|
||||
font-weight: var(--canon-font-weight-regular);
|
||||
color: var(--canon-fg-primary);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
cursor: inherit;
|
||||
align-items: center;
|
||||
transition: border-color .2s ease-in-out, outline-color .2s ease-in-out;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.canon-TextFieldInput::placeholder {
|
||||
color: var(--canon-fg-secondary);
|
||||
}
|
||||
|
||||
.canon-TextFieldInput[data-icon] {
|
||||
padding-left: var(--canon-space-8);
|
||||
}
|
||||
|
||||
.canon-TextFieldInput[data-focused] {
|
||||
outline-color: var(--canon-border-pressed);
|
||||
outline-width: 0;
|
||||
}
|
||||
|
||||
.canon-TextFieldInput[data-hovered] {
|
||||
border-color: var(--canon-border-hover);
|
||||
}
|
||||
|
||||
.canon-TextFieldInput[data-focused] {
|
||||
border-color: var(--canon-border-pressed);
|
||||
outline-width: 0;
|
||||
}
|
||||
|
||||
.canon-TextFieldInput[data-invalid] {
|
||||
border-color: var(--canon-fg-danger);
|
||||
}
|
||||
|
||||
.canon-TextFieldInput[data-disabled] {
|
||||
opacity: .5;
|
||||
cursor: not-allowed;
|
||||
border: 1px solid var(--canon-border-disabled);
|
||||
}
|
||||
|
||||
.canon-TextFieldError {
|
||||
color: var(--canon-fg-danger);
|
||||
font-size: var(--canon-font-size-2);
|
||||
font-weight: var(--canon-font-weight-regular);
|
||||
margin-top: var(--canon-space-2);
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
.canon-TooltipPopup {
|
||||
box-sizing: border-box;
|
||||
transform-origin: var(--transform-origin);
|
||||
background-color: canvas;
|
||||
background-color: var(--canon-bg-surface-1);
|
||||
color: var(--canon-fg-primary);
|
||||
outline: 1px solid var(--canon-border);
|
||||
box-shadow: 0 10px 15px -3px var(--canon-border), 0 4px 6px -4px var(--canon-border);
|
||||
border-radius: .375rem;
|
||||
flex-direction: column;
|
||||
padding: .25rem .5rem;
|
||||
font-size: .875rem;
|
||||
line-height: 1.25rem;
|
||||
transition: transform .15s, opacity .15s;
|
||||
display: flex;
|
||||
|
||||
&[data-starting-style], &[data-ending-style] {
|
||||
opacity: 0;
|
||||
transform: scale(.9);
|
||||
}
|
||||
|
||||
&[data-instant] {
|
||||
transition-duration: 0s;
|
||||
}
|
||||
}
|
||||
|
||||
.canon-TooltipArrow {
|
||||
display: flex;
|
||||
|
||||
&[data-side="top"] {
|
||||
bottom: -8px;
|
||||
rotate: 180deg;
|
||||
}
|
||||
|
||||
&[data-side="bottom"] {
|
||||
top: -8px;
|
||||
rotate: none;
|
||||
}
|
||||
|
||||
&[data-side="left"] {
|
||||
right: -13px;
|
||||
rotate: 90deg;
|
||||
}
|
||||
|
||||
&[data-side="right"] {
|
||||
left: -13px;
|
||||
rotate: -90deg;
|
||||
}
|
||||
}
|
||||
|
||||
.canon-TooltipArrow-fill {
|
||||
fill: var(--canon-bg-surface-1);
|
||||
}
|
||||
|
||||
.canon-TooltipArrow-outer-stroke {
|
||||
@media (prefers-color-scheme: light) {
|
||||
& {
|
||||
fill: var(--canon-border);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,8 @@ import { HTMLAttributes } from 'react';
|
||||
import { JSX as JSX_2 } from 'react/jsx-runtime';
|
||||
import { LinkProps as LinkProps_2 } from 'react-aria-components';
|
||||
import { Menu as Menu_2 } from '@base-ui-components/react/menu';
|
||||
import type { RadioGroupProps as RadioGroupProps_2 } from 'react-aria-components';
|
||||
import type { RadioProps as RadioProps_2 } from 'react-aria-components';
|
||||
import { ReactElement } from 'react';
|
||||
import { ReactNode } from 'react';
|
||||
import { RefAttributes } from 'react';
|
||||
@@ -1285,6 +1287,27 @@ export type PositionProps = GetPropDefTypes<typeof positionPropDefs>;
|
||||
// @public (undocumented)
|
||||
export type PropDef<T = any> = RegularPropDef<T> | ResponsivePropDef<T>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const Radio: ForwardRefExoticComponent<
|
||||
RadioProps & RefAttributes<HTMLLabelElement>
|
||||
>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const RadioGroup: ForwardRefExoticComponent<
|
||||
RadioGroupProps & RefAttributes<HTMLDivElement>
|
||||
>;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface RadioGroupProps
|
||||
extends Omit<RadioGroupProps_2, 'children'>,
|
||||
Omit<FieldLabelProps, 'htmlFor' | 'id'> {
|
||||
// (undocumented)
|
||||
children?: ReactNode;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface RadioProps extends RadioProps_2 {}
|
||||
|
||||
// @public (undocumented)
|
||||
export type ReactNodePropDef = {
|
||||
type: 'ReactNode';
|
||||
@@ -1552,6 +1575,7 @@ export interface TextFieldProps
|
||||
extends TextFieldProps_2,
|
||||
Omit<FieldLabelProps, 'htmlFor' | 'id'> {
|
||||
icon?: ReactNode;
|
||||
placeholder?: string;
|
||||
size?: 'small' | 'medium' | Partial<Record<Breakpoint, 'small' | 'medium'>>;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,89 +17,55 @@
|
||||
/* eslint-disable no-restricted-imports */
|
||||
import { transform, bundle } from 'lightningcss';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import chalk from 'chalk';
|
||||
import { glob } from 'glob';
|
||||
/* eslint-enable no-restricted-imports */
|
||||
|
||||
// Check if core.css and components.css exist
|
||||
const cssDir = 'src/css';
|
||||
const srcFile = 'src/css/styles.css';
|
||||
const distDir = 'css';
|
||||
const componentsDir = 'src/components';
|
||||
const distFile = `${distDir}/styles.css`;
|
||||
|
||||
// Core files
|
||||
const cssFiles = [
|
||||
{ path: `${cssDir}/core.css`, newName: 'core.css' },
|
||||
{ path: `${cssDir}/components.css`, newName: 'components.css' },
|
||||
{ path: `${cssDir}/styles.css`, newName: 'styles.css' },
|
||||
];
|
||||
|
||||
// Components files
|
||||
const componentsFiles = glob
|
||||
.sync('**/*.css', { cwd: componentsDir })
|
||||
.map(file => {
|
||||
const folderName = file.split('/')[0].toLocaleLowerCase('en-US');
|
||||
return { path: `${componentsDir}/${file}`, newName: `${folderName}.css` };
|
||||
});
|
||||
|
||||
// Combine core and components files
|
||||
cssFiles.push(...componentsFiles);
|
||||
|
||||
// Check if files exist
|
||||
cssFiles.forEach(file => {
|
||||
if (!fs.existsSync(file.path)) {
|
||||
console.error(`${file.originalName} does not exist`);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
// Check if styles.css exists
|
||||
if (!fs.existsSync(srcFile)) {
|
||||
console.error(`${srcFile} does not exist`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Ensure the dist/css directory exists
|
||||
if (!fs.existsSync(distDir)) {
|
||||
fs.mkdirSync(distDir, { recursive: true });
|
||||
}
|
||||
|
||||
// Add watch mode support
|
||||
const args = process.argv.slice(2);
|
||||
const watchMode = args.includes('--watch');
|
||||
|
||||
async function buildCSS(logs = true) {
|
||||
// Bundle and transform files
|
||||
cssFiles.forEach(file => {
|
||||
let { code: bundleCode } = bundle({
|
||||
filename: file.path,
|
||||
});
|
||||
const css = fs.readFileSync(srcFile);
|
||||
|
||||
let { code } = transform({
|
||||
filename: `${distDir}/${file.newName}`,
|
||||
code: bundleCode,
|
||||
});
|
||||
|
||||
fs.writeFileSync(`${distDir}/${file.newName}`, code);
|
||||
|
||||
if (logs) {
|
||||
console.log(chalk.blue('CSS bundled: ') + file.newName);
|
||||
}
|
||||
let { code: bundleCode } = bundle({
|
||||
filename: srcFile,
|
||||
});
|
||||
|
||||
const { code } = transform({
|
||||
filename: distFile,
|
||||
code: bundleCode,
|
||||
minify: false,
|
||||
});
|
||||
fs.writeFileSync(distFile, code);
|
||||
if (logs) {
|
||||
console.log(chalk.green('CSS files bundled successfully!'));
|
||||
console.log(chalk.blue('CSS transformed and minified: ') + 'styles.css');
|
||||
console.log(chalk.green('CSS file built successfully!'));
|
||||
}
|
||||
}
|
||||
|
||||
if (watchMode) {
|
||||
// Watch both directories for changes
|
||||
[cssDir, componentsDir].forEach(dir => {
|
||||
fs.watch(dir, { recursive: true }, (eventType, filename) => {
|
||||
if (filename?.endsWith('.css')) {
|
||||
console.log(
|
||||
chalk.yellow(`Changes detected in ${filename}, rebuilding...`),
|
||||
);
|
||||
buildCSS(false).catch(console.error);
|
||||
}
|
||||
});
|
||||
fs.watch('src/css', { recursive: true }, (eventType, filename) => {
|
||||
if (filename === 'styles.css') {
|
||||
console.log(
|
||||
chalk.yellow(`Changes detected in ${filename}, rebuilding...`),
|
||||
);
|
||||
buildCSS(false).catch(console.error);
|
||||
}
|
||||
});
|
||||
|
||||
// Initial build
|
||||
buildCSS().catch(console.error);
|
||||
console.log(chalk.yellow('Watching for CSS changes...'));
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { TextField, Input, Form } from 'react-aria-components';
|
||||
import { FieldError } from './FieldError';
|
||||
|
||||
const meta = {
|
||||
title: 'Forms/FieldError',
|
||||
component: FieldError,
|
||||
} satisfies Meta<typeof FieldError>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
// Show error with server validation using Form component
|
||||
export const WithServerValidation: Story = {
|
||||
render: () => (
|
||||
<Form validationErrors={{ demo: 'This is a server validation error.' }}>
|
||||
<TextField
|
||||
name="demo"
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'flex-start',
|
||||
}}
|
||||
>
|
||||
<Input />
|
||||
<FieldError />
|
||||
</TextField>
|
||||
</Form>
|
||||
),
|
||||
};
|
||||
|
||||
// Show error using children
|
||||
export const WithCustomMessage: Story = {
|
||||
render: () => (
|
||||
<TextField
|
||||
isInvalid
|
||||
validationBehavior="aria"
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'flex-start',
|
||||
}}
|
||||
>
|
||||
<Input />
|
||||
<FieldError>This is a custom error message.</FieldError>
|
||||
</TextField>
|
||||
),
|
||||
};
|
||||
|
||||
// Show error with render prop function
|
||||
export const WithRenderProp: Story = {
|
||||
render: () => (
|
||||
<TextField
|
||||
isInvalid
|
||||
validationBehavior="aria"
|
||||
validate={() => 'This field is invalid'}
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'flex-start',
|
||||
}}
|
||||
>
|
||||
<Input />
|
||||
<FieldError>
|
||||
{({ validationErrors }) =>
|
||||
validationErrors.length > 0 ? validationErrors[0] : 'Field is invalid'
|
||||
}
|
||||
</FieldError>
|
||||
</TextField>
|
||||
),
|
||||
};
|
||||
@@ -0,0 +1,6 @@
|
||||
.canon-FieldError {
|
||||
color: var(--canon-fg-danger);
|
||||
font-size: var(--canon-font-size-2);
|
||||
font-weight: var(--canon-font-weight-regular);
|
||||
margin-top: var(--canon-space-2);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { forwardRef } from 'react';
|
||||
import {
|
||||
FieldError as AriaFieldError,
|
||||
type FieldErrorProps,
|
||||
} from 'react-aria-components';
|
||||
import clsx from 'clsx';
|
||||
|
||||
/** @public */
|
||||
export const FieldError = forwardRef<HTMLDivElement, FieldErrorProps>(
|
||||
(props: FieldErrorProps, ref) => {
|
||||
const { className, ...rest } = props;
|
||||
|
||||
return (
|
||||
<AriaFieldError
|
||||
className={clsx('canon-FieldError', className)}
|
||||
ref={ref}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
FieldError.displayName = 'FieldError';
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './FieldError';
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { RadioGroup, Radio } from './RadioGroup';
|
||||
|
||||
const meta = {
|
||||
title: 'Forms/RadioGroup',
|
||||
component: RadioGroup,
|
||||
} satisfies Meta<typeof RadioGroup>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
label: 'What is your favorite pokemon?',
|
||||
},
|
||||
render: args => (
|
||||
<RadioGroup {...args}>
|
||||
<Radio value="bulbasaur">Bulbasaur</Radio>
|
||||
<Radio value="charmander">Charmander</Radio>
|
||||
<Radio value="squirtle">Squirtle</Radio>
|
||||
</RadioGroup>
|
||||
),
|
||||
};
|
||||
|
||||
export const Horizontal: Story = {
|
||||
args: {
|
||||
...Default.args,
|
||||
orientation: 'horizontal',
|
||||
},
|
||||
render: args => (
|
||||
<RadioGroup {...args}>
|
||||
<Radio value="bulbasaur">Bulbasaur</Radio>
|
||||
<Radio value="charmander">Charmander</Radio>
|
||||
<Radio value="squirtle">Squirtle</Radio>
|
||||
</RadioGroup>
|
||||
),
|
||||
};
|
||||
|
||||
export const Disabled: Story = {
|
||||
args: {
|
||||
...Default.args,
|
||||
isDisabled: true,
|
||||
},
|
||||
render: args => (
|
||||
<RadioGroup {...args}>
|
||||
<Radio value="bulbasaur">Bulbasaur</Radio>
|
||||
<Radio value="charmander">Charmander</Radio>
|
||||
<Radio value="squirtle">Squirtle</Radio>
|
||||
</RadioGroup>
|
||||
),
|
||||
};
|
||||
|
||||
export const DisabledSingle: Story = {
|
||||
args: {
|
||||
...Default.args,
|
||||
},
|
||||
render: args => (
|
||||
<RadioGroup {...args}>
|
||||
<Radio value="bulbasaur">Bulbasaur</Radio>
|
||||
<Radio value="charmander" isDisabled>
|
||||
Charmander
|
||||
</Radio>
|
||||
<Radio value="squirtle">Squirtle</Radio>
|
||||
</RadioGroup>
|
||||
),
|
||||
};
|
||||
|
||||
export const DisabledAndSelected: Story = {
|
||||
args: {
|
||||
...Default.args,
|
||||
value: 'charmander',
|
||||
},
|
||||
render: args => (
|
||||
<RadioGroup {...args}>
|
||||
<Radio value="bulbasaur">Bulbasaur</Radio>
|
||||
<Radio value="charmander" isDisabled>
|
||||
Charmander
|
||||
</Radio>
|
||||
<Radio value="squirtle">Squirtle</Radio>
|
||||
</RadioGroup>
|
||||
),
|
||||
};
|
||||
|
||||
export const Invalid: Story = {
|
||||
args: {
|
||||
...Default.args,
|
||||
name: 'pokemon',
|
||||
isInvalid: true,
|
||||
},
|
||||
render: args => (
|
||||
<RadioGroup {...args}>
|
||||
<Radio value="bulbasaur">Bulbasaur</Radio>
|
||||
<Radio value="charmander" isDisabled>
|
||||
Charmander
|
||||
</Radio>
|
||||
<Radio value="squirtle">Squirtle</Radio>
|
||||
</RadioGroup>
|
||||
),
|
||||
};
|
||||
|
||||
export const Validation: Story = {
|
||||
args: {
|
||||
...Default.args,
|
||||
name: 'pokemon',
|
||||
defaultValue: 'charmander',
|
||||
validationBehavior: 'aria',
|
||||
validate: value => (value === 'charmander' ? 'Nice try!' : null),
|
||||
},
|
||||
render: args => (
|
||||
<RadioGroup {...args}>
|
||||
<Radio value="bulbasaur">Bulbasaur</Radio>
|
||||
<Radio value="charmander">Charmander</Radio>
|
||||
<Radio value="squirtle">Squirtle</Radio>
|
||||
</RadioGroup>
|
||||
),
|
||||
};
|
||||
|
||||
export const ReadOnly: Story = {
|
||||
args: {
|
||||
...Default.args,
|
||||
isReadOnly: true,
|
||||
defaultValue: 'charmander',
|
||||
},
|
||||
render: args => (
|
||||
<RadioGroup {...args}>
|
||||
<Radio value="bulbasaur">Bulbasaur</Radio>
|
||||
<Radio value="charmander">Charmander</Radio>
|
||||
<Radio value="squirtle">Squirtle</Radio>
|
||||
</RadioGroup>
|
||||
),
|
||||
};
|
||||
@@ -0,0 +1,95 @@
|
||||
.canon-RadioGroup {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: var(--canon-fg-primary);
|
||||
}
|
||||
|
||||
.canon-RadioGroup[data-orientation='horizontal'] .canon-RadioGroupContent {
|
||||
flex-direction: row;
|
||||
gap: var(--canon-space-4);
|
||||
}
|
||||
|
||||
.canon-RadioGroupContent {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--canon-space-2);
|
||||
}
|
||||
|
||||
.canon-Radio {
|
||||
display: flex;
|
||||
/* This is needed so the HiddenInput is positioned correctly */
|
||||
position: relative;
|
||||
align-items: center;
|
||||
gap: var(--canon-space-2);
|
||||
font-size: var(--canon-font-size-2);
|
||||
color: var(--canon-fg-primary);
|
||||
forced-color-adjust: none;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
box-sizing: border-box;
|
||||
border: 0.125rem solid var(--canon-border);
|
||||
background: var(--canon-gray-1);
|
||||
border-radius: var(--canon-radius-full);
|
||||
transition: all 200ms;
|
||||
}
|
||||
|
||||
&[data-pressed]:before {
|
||||
border-color: var(--canon-border);
|
||||
}
|
||||
|
||||
&[data-selected] {
|
||||
&:before {
|
||||
border-color: var(--canon-bg-solid);
|
||||
border-width: 0.25rem;
|
||||
}
|
||||
|
||||
&[data-pressed]:before {
|
||||
border-color: var(--canon-bg-solid);
|
||||
}
|
||||
}
|
||||
|
||||
&[data-focus-visible]:before {
|
||||
outline: 2px solid var(--canon-ring);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
&[data-disabled] {
|
||||
cursor: not-allowed;
|
||||
color: var(--canon-fg-disabled);
|
||||
|
||||
&:before {
|
||||
border-color: var(--canon-border-disabled);
|
||||
background: var(--canon-bg-disabled);
|
||||
}
|
||||
|
||||
&[data-selected]:before {
|
||||
border-color: var(--canon-border-disabled);
|
||||
}
|
||||
}
|
||||
|
||||
&[data-invalid]:before {
|
||||
border-color: var(--canon-border-danger);
|
||||
}
|
||||
|
||||
&[data-invalid][data-selected]:before {
|
||||
border-color: var(--canon-border-danger);
|
||||
}
|
||||
|
||||
/* Ensure disabled state prevails over invalid state */
|
||||
&[data-disabled][data-invalid] {
|
||||
color: var(--canon-fg-disabled);
|
||||
|
||||
&:before {
|
||||
border-color: var(--canon-border-disabled);
|
||||
background: var(--canon-bg-disabled);
|
||||
}
|
||||
|
||||
&[data-selected]:before {
|
||||
border-color: var(--canon-border-disabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { forwardRef, useEffect } from 'react';
|
||||
import {
|
||||
RadioGroup as AriaRadioGroup,
|
||||
Radio as AriaRadio,
|
||||
} from 'react-aria-components';
|
||||
import clsx from 'clsx';
|
||||
import { FieldLabel } from '../FieldLabel';
|
||||
import { FieldError } from '../FieldError';
|
||||
|
||||
import type { RadioGroupProps, RadioProps } from './types';
|
||||
|
||||
/** @public */
|
||||
export const RadioGroup = forwardRef<HTMLDivElement, RadioGroupProps>(
|
||||
(props, ref) => {
|
||||
const {
|
||||
className,
|
||||
label,
|
||||
secondaryLabel,
|
||||
description,
|
||||
isRequired,
|
||||
'aria-label': ariaLabel,
|
||||
'aria-labelledby': ariaLabelledBy,
|
||||
children,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
useEffect(() => {
|
||||
if (!label && !ariaLabel && !ariaLabelledBy) {
|
||||
console.warn(
|
||||
'RadioGroup requires either a visible label, aria-label, or aria-labelledby for accessibility',
|
||||
);
|
||||
}
|
||||
}, [label, ariaLabel, ariaLabelledBy]);
|
||||
|
||||
// If a secondary label is provided, use it. Otherwise, use 'Required' if the field is required.
|
||||
const secondaryLabelText =
|
||||
secondaryLabel || (isRequired ? 'Required' : null);
|
||||
|
||||
return (
|
||||
<AriaRadioGroup
|
||||
className={clsx('canon-RadioGroup', className)}
|
||||
aria-label={ariaLabel}
|
||||
aria-labelledby={ariaLabelledBy}
|
||||
{...rest}
|
||||
ref={ref}
|
||||
>
|
||||
<FieldLabel
|
||||
label={label}
|
||||
secondaryLabel={secondaryLabelText}
|
||||
description={description}
|
||||
/>
|
||||
<div className="canon-RadioGroupContent">{children}</div>
|
||||
<FieldError />
|
||||
</AriaRadioGroup>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
RadioGroup.displayName = 'RadioGroup';
|
||||
|
||||
/** @public */
|
||||
export const Radio = forwardRef<HTMLLabelElement, RadioProps>((props, ref) => {
|
||||
const { className, ...rest } = props;
|
||||
|
||||
return (
|
||||
<AriaRadio className={clsx('canon-Radio', className)} {...rest} ref={ref} />
|
||||
);
|
||||
});
|
||||
|
||||
RadioGroup.displayName = 'RadioGroup';
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './RadioGroup';
|
||||
export * from './types';
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type {
|
||||
RadioGroupProps as AriaRadioGroupProps,
|
||||
RadioProps as AriaRadioProps,
|
||||
} from 'react-aria-components';
|
||||
import type { FieldLabelProps } from '../FieldLabel/types';
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
/** @public */
|
||||
export interface RadioGroupProps
|
||||
extends Omit<AriaRadioGroupProps, 'children'>,
|
||||
Omit<FieldLabelProps, 'htmlFor' | 'id'> {
|
||||
children?: ReactNode;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface RadioProps extends AriaRadioProps {}
|
||||
@@ -40,7 +40,7 @@ type Story = StoryObj<typeof meta>;
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
name: 'url',
|
||||
defaultValue: 'Enter a URL',
|
||||
placeholder: 'Enter a URL',
|
||||
style: {
|
||||
maxWidth: '300px',
|
||||
},
|
||||
@@ -97,7 +97,7 @@ export const Disabled: Story = {
|
||||
export const WithIcon: Story = {
|
||||
args: {
|
||||
...Default.args,
|
||||
defaultValue: 'Search...',
|
||||
placeholder: 'Search...',
|
||||
icon: <Icon name="search" />,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -104,10 +104,3 @@
|
||||
cursor: not-allowed;
|
||||
border: 1px solid var(--canon-border-disabled);
|
||||
}
|
||||
|
||||
.canon-TextFieldError {
|
||||
color: var(--canon-fg-danger);
|
||||
font-size: var(--canon-font-size-2);
|
||||
font-weight: var(--canon-font-weight-regular);
|
||||
margin-top: var(--canon-space-2);
|
||||
}
|
||||
|
||||
@@ -15,14 +15,11 @@
|
||||
*/
|
||||
|
||||
import { forwardRef, useEffect } from 'react';
|
||||
import {
|
||||
Input,
|
||||
TextField as AriaTextField,
|
||||
FieldError,
|
||||
} from 'react-aria-components';
|
||||
import { Input, TextField as AriaTextField } from 'react-aria-components';
|
||||
import { useResponsiveValue } from '../../hooks/useResponsiveValue';
|
||||
import clsx from 'clsx';
|
||||
import { FieldLabel } from '../FieldLabel';
|
||||
import { FieldError } from '../FieldError';
|
||||
|
||||
import type { TextFieldProps } from './types';
|
||||
|
||||
@@ -39,6 +36,7 @@ export const TextField = forwardRef<HTMLDivElement, TextFieldProps>(
|
||||
isRequired,
|
||||
'aria-label': ariaLabel,
|
||||
'aria-labelledby': ariaLabelledBy,
|
||||
placeholder,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
@@ -84,9 +82,10 @@ export const TextField = forwardRef<HTMLDivElement, TextFieldProps>(
|
||||
<Input
|
||||
className="canon-TextFieldInput"
|
||||
{...(icon && { 'data-icon': true })}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
</div>
|
||||
<FieldError className="canon-TextFieldError" />
|
||||
<FieldError />
|
||||
</AriaTextField>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -33,4 +33,9 @@ export interface TextFieldProps
|
||||
* @defaultValue 'medium'
|
||||
*/
|
||||
size?: 'small' | 'medium' | Partial<Record<Breakpoint, 'small' | 'medium'>>;
|
||||
|
||||
/**
|
||||
* Text to display in the input when it has no value
|
||||
*/
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
@import '../components/Container/styles.css';
|
||||
@import '../components/DataTable/Root/DataTableRoot.styles.css';
|
||||
@import '../components/DataTable/Pagination/DataTablePagination.styles.css';
|
||||
@import '../components/FieldError/FieldError.styles.css';
|
||||
@import '../components/FieldLabel/FieldLabel.styles.css';
|
||||
@import '../components/Flex/styles.css';
|
||||
@import '../components/Grid/styles.css';
|
||||
@@ -30,6 +31,7 @@
|
||||
@import '../components/Icon/styles.css';
|
||||
@import '../components/Link/styles.css';
|
||||
@import '../components/Menu/Menu.styles.css';
|
||||
@import '../components/RadioGroup/RadioGroup.styles.css';
|
||||
@import '../components/Table/styles.css';
|
||||
@import '../components/Table/TableCell/TableCell.styles.css';
|
||||
@import '../components/Table/TableCellText/TableCellText.styles.css';
|
||||
|
||||
@@ -41,6 +41,7 @@ export * from './components/Icon';
|
||||
export * from './components/ButtonIcon';
|
||||
export * from './components/ButtonLink';
|
||||
export * from './components/Checkbox';
|
||||
export * from './components/RadioGroup';
|
||||
export * from './components/Table';
|
||||
export * from './components/Tabs';
|
||||
export * from './components/TextField';
|
||||
|
||||
@@ -173,7 +173,7 @@ export class ScmAuth implements ScmAuthApi {
|
||||
const host = options?.host ?? 'gitlab.com';
|
||||
return new ScmAuth('gitlab', gitlabAuthApi, host, {
|
||||
default: ['read_user', 'read_api', 'read_repository'],
|
||||
repoWrite: ['write_repository api'],
|
||||
repoWrite: ['write_repository', 'api'],
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -5,13 +5,13 @@ module.exports = require('@backstage/cli/config/eslint-factory')(__dirname, {
|
||||
name: 'path',
|
||||
importNames: ['resolve'],
|
||||
message:
|
||||
'Do not use path.resolve, use `resolveSafeChildPath` from `@backstage/backend-common` instead as it prevents security issues',
|
||||
'Do not use path.resolve, use `resolveSafeChildPath` from `@backstage/backend-plugin-api` instead as it prevents security issues',
|
||||
},
|
||||
],
|
||||
restrictedSrcSyntax: [
|
||||
{
|
||||
message:
|
||||
'Do not use path.resolve, use `resolveSafeChildPath` from `@backstage/backend-common` instead as it prevents security issues',
|
||||
'Do not use path.resolve, use `resolveSafeChildPath` from `@backstage/backend-plugin-api` instead as it prevents security issues',
|
||||
selector: 'MemberExpression[object.name="path"][property.name="resolve"]',
|
||||
},
|
||||
],
|
||||
|
||||
@@ -61,7 +61,6 @@
|
||||
"test": "backstage-cli package test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/backend-common": "^0.25.0",
|
||||
"@backstage/backend-defaults": "workspace:^",
|
||||
"@backstage/backend-plugin-api": "workspace:^",
|
||||
"@backstage/catalog-model": "workspace:^",
|
||||
|
||||
@@ -14,12 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { DatabaseManager } from '@backstage/backend-common';
|
||||
import { DatabaseManager } from '@backstage/backend-defaults/database';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { DatabaseTaskStore, RawDbTaskEventRow } from './DatabaseTaskStore';
|
||||
import { TaskSpec } from '@backstage/plugin-scaffolder-common';
|
||||
import { ConflictError } from '@backstage/errors';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
import {
|
||||
mockServices,
|
||||
createMockDirectory,
|
||||
} from '@backstage/backend-test-utils';
|
||||
import fs from 'fs-extra';
|
||||
import { EventsService } from '@backstage/plugin-events-node';
|
||||
|
||||
@@ -33,7 +36,10 @@ const createStore = async (events?: EventsService) => {
|
||||
},
|
||||
},
|
||||
}),
|
||||
).forPlugin('scaffolder');
|
||||
).forPlugin('scaffolder', {
|
||||
logger: mockServices.logger.mock(),
|
||||
lifecycle: mockServices.lifecycle.mock(),
|
||||
});
|
||||
const store = await DatabaseTaskStore.create({
|
||||
database: manager,
|
||||
events,
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { DatabaseManager } from '@backstage/backend-common';
|
||||
import { DatabaseManager } from '@backstage/backend-defaults/database';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { TaskSpec } from '@backstage/plugin-scaffolder-common';
|
||||
import {
|
||||
@@ -36,7 +36,10 @@ async function createStore(): Promise<DatabaseTaskStore> {
|
||||
},
|
||||
},
|
||||
}),
|
||||
).forPlugin('scaffolder');
|
||||
).forPlugin('scaffolder', {
|
||||
logger: mockServices.logger.mock(),
|
||||
lifecycle: mockServices.lifecycle.mock(),
|
||||
});
|
||||
|
||||
return await DatabaseTaskStore.create({
|
||||
database: manager,
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import os from 'os';
|
||||
import { DatabaseManager } from '@backstage/backend-common';
|
||||
import { DatabaseManager } from '@backstage/backend-defaults/database';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { DatabaseTaskStore } from './DatabaseTaskStore';
|
||||
import { StorageTaskBroker } from './StorageTaskBroker';
|
||||
@@ -49,7 +49,10 @@ async function createStore(): Promise<DatabaseTaskStore> {
|
||||
},
|
||||
},
|
||||
}),
|
||||
).forPlugin('scaffolder');
|
||||
).forPlugin('scaffolder', {
|
||||
logger: mockServices.logger.mock(),
|
||||
lifecycle: mockServices.lifecycle.mock(),
|
||||
});
|
||||
return await DatabaseTaskStore.create({
|
||||
database: manager,
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { DatabaseManager } from '@backstage/backend-common';
|
||||
import { DatabaseManager } from '@backstage/backend-defaults/database';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import request from 'supertest';
|
||||
import ObservableImpl from 'zen-observable';
|
||||
@@ -77,7 +77,10 @@ function createDatabase(): DatabaseService {
|
||||
},
|
||||
},
|
||||
}),
|
||||
).forPlugin('scaffolder');
|
||||
).forPlugin('scaffolder', {
|
||||
logger: mockServices.logger.mock(),
|
||||
lifecycle: mockServices.lifecycle.mock(),
|
||||
});
|
||||
}
|
||||
|
||||
const config = new ConfigReader({});
|
||||
|
||||
@@ -7499,7 +7499,6 @@ __metadata:
|
||||
resolution: "@backstage/plugin-scaffolder-backend@workspace:plugins/scaffolder-backend"
|
||||
dependencies:
|
||||
"@backstage/backend-app-api": "workspace:^"
|
||||
"@backstage/backend-common": "npm:^0.25.0"
|
||||
"@backstage/backend-defaults": "workspace:^"
|
||||
"@backstage/backend-plugin-api": "workspace:^"
|
||||
"@backstage/backend-test-utils": "workspace:^"
|
||||
@@ -8757,9 +8756,9 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@changesets/assemble-release-plan@npm:^6.0.8":
|
||||
version: 6.0.8
|
||||
resolution: "@changesets/assemble-release-plan@npm:6.0.8"
|
||||
"@changesets/assemble-release-plan@npm:^6.0.9":
|
||||
version: 6.0.9
|
||||
resolution: "@changesets/assemble-release-plan@npm:6.0.9"
|
||||
dependencies:
|
||||
"@changesets/errors": "npm:^0.2.0"
|
||||
"@changesets/get-dependents-graph": "npm:^2.1.3"
|
||||
@@ -8767,7 +8766,7 @@ __metadata:
|
||||
"@changesets/types": "npm:^6.1.0"
|
||||
"@manypkg/get-packages": "npm:^1.1.3"
|
||||
semver: "npm:^7.5.3"
|
||||
checksum: 10/5d01fc42c67229874cc70b93fbdc971e11909aa7a72f1909c585ecb3fdc69f3ac105d243e1341cd5b07c02dee133be461fa48138125f00d137e71f8b7e8f428e
|
||||
checksum: 10/f84656eabb700ed77f97751b282e1701636ed45a44b443abd9af0291870495cc046fee301478010f39a1dc455799065ae007b9d7d2bb5ae8b793b65bbb8e052a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -8781,16 +8780,16 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"@changesets/cli@npm:^2.14.0":
|
||||
version: 2.29.4
|
||||
resolution: "@changesets/cli@npm:2.29.4"
|
||||
version: 2.29.5
|
||||
resolution: "@changesets/cli@npm:2.29.5"
|
||||
dependencies:
|
||||
"@changesets/apply-release-plan": "npm:^7.0.12"
|
||||
"@changesets/assemble-release-plan": "npm:^6.0.8"
|
||||
"@changesets/assemble-release-plan": "npm:^6.0.9"
|
||||
"@changesets/changelog-git": "npm:^0.2.1"
|
||||
"@changesets/config": "npm:^3.1.1"
|
||||
"@changesets/errors": "npm:^0.2.0"
|
||||
"@changesets/get-dependents-graph": "npm:^2.1.3"
|
||||
"@changesets/get-release-plan": "npm:^4.0.12"
|
||||
"@changesets/get-release-plan": "npm:^4.0.13"
|
||||
"@changesets/git": "npm:^3.0.4"
|
||||
"@changesets/logger": "npm:^0.1.1"
|
||||
"@changesets/pre": "npm:^2.0.2"
|
||||
@@ -8814,7 +8813,7 @@ __metadata:
|
||||
term-size: "npm:^2.1.0"
|
||||
bin:
|
||||
changeset: bin.js
|
||||
checksum: 10/fc325447b81a811464107e72a687f6c0414c5f928e518cb122d1efde1d71c205b1972464795ab97fb26087900ea55b99a551e9a010c9681def3d7561fd1c3f0b
|
||||
checksum: 10/f401da29025d7bcc07b732bb09a9627f785bfc21c7c2005861d11ffea732bc14d33394fc2fcae50cc5f2b710f6080c5babe2fa90d432de5fdb47ae6afc147936
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -8854,17 +8853,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@changesets/get-release-plan@npm:^4.0.12":
|
||||
version: 4.0.12
|
||||
resolution: "@changesets/get-release-plan@npm:4.0.12"
|
||||
"@changesets/get-release-plan@npm:^4.0.13":
|
||||
version: 4.0.13
|
||||
resolution: "@changesets/get-release-plan@npm:4.0.13"
|
||||
dependencies:
|
||||
"@changesets/assemble-release-plan": "npm:^6.0.8"
|
||||
"@changesets/assemble-release-plan": "npm:^6.0.9"
|
||||
"@changesets/config": "npm:^3.1.1"
|
||||
"@changesets/pre": "npm:^2.0.2"
|
||||
"@changesets/read": "npm:^0.6.5"
|
||||
"@changesets/types": "npm:^6.1.0"
|
||||
"@manypkg/get-packages": "npm:^1.1.3"
|
||||
checksum: 10/d6482ecb6f1c2c47266493a36d05b484f0950d0a4472820649e953d073e3fdd612cdd8a4df9e3d7e00756d4e446dae639f9d6e0dab8a25f76bb6df77cd91c21c
|
||||
checksum: 10/9983fae5a68012c4c418ddd62f2fb3d325363f21160252ff7b868503a1a2effb8fdd32e4a0289b72653afc3605ce19d163ff69205c942a0004efb571a5f78fd0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -9105,11 +9104,11 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"@dagrejs/dagre@npm:^1.1.4":
|
||||
version: 1.1.4
|
||||
resolution: "@dagrejs/dagre@npm:1.1.4"
|
||||
version: 1.1.5
|
||||
resolution: "@dagrejs/dagre@npm:1.1.5"
|
||||
dependencies:
|
||||
"@dagrejs/graphlib": "npm:2.2.4"
|
||||
checksum: 10/0b3744b170c68ae0666e03aca19c3100d5131feafeb54b3ea096b749a9f0fe5385b8bd8889c11a49493cfab945b2486b9e30bc41b321755ed718e9f5cb4b74f1
|
||||
checksum: 10/c00abd1e04d19f90ad8dfa0a4e16365371bc4309affead3827a1b39f6b0b946643b8af0b1e5519011deca3fda4c7471b27e9ebb03423309a94f95ac0b881ac4f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -14865,13 +14864,13 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"@playwright/test@npm:^1.32.3":
|
||||
version: 1.53.0
|
||||
resolution: "@playwright/test@npm:1.53.0"
|
||||
version: 1.53.1
|
||||
resolution: "@playwright/test@npm:1.53.1"
|
||||
dependencies:
|
||||
playwright: "npm:1.53.0"
|
||||
playwright: "npm:1.53.1"
|
||||
bin:
|
||||
playwright: cli.js
|
||||
checksum: 10/968df4fba133dd18b8c65504c3cc5a3a6071e49f0706c6524711cdfab321a51debfeb506b9ff0a8f7dd8ce3015921d82fa51429d8f11d392cc68de1938703c33
|
||||
checksum: 10/98fb9b962710183d465b695daab2006296fd9a703ecb1b763a38cd12a39f7d6066f9539d1758e54313d393353fafb16b90fb31e4add1ca99ffec99b8b1b40fb9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -21782,9 +21781,9 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"@types/lodash@npm:^4.14.151":
|
||||
version: 4.17.17
|
||||
resolution: "@types/lodash@npm:4.17.17"
|
||||
checksum: 10/496459a3cb1a0733bb60532de3899ad6297717af0b9b26ad6821154b2005fec86f29ccd47a2e6f4da4a8c7c818bb8ae73901144e8057ea86b7b02a3d7bb9d13f
|
||||
version: 4.17.18
|
||||
resolution: "@types/lodash@npm:4.17.18"
|
||||
checksum: 10/54ebb15b29925112dbe9da3abd99fb80d7202bc5ba20fc1b4fc8ea835d0012f00cbd9a3e7f367b70e7c3f2d5ee635964e3920a489625647b558f02994b3dd381
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -24385,13 +24384,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"GendocuPublicApis@https://git.gendocu.com/gendocu/GendocuPublicApis.git#master":
|
||||
version: 1.0.0
|
||||
resolution: "GendocuPublicApis@https://git.gendocu.com/gendocu/GendocuPublicApis.git#commit=6e8d4c1d2342556a2e8e719f19385b266001ebae"
|
||||
"GendocuPublicApis@npm:gendocu-public-apis@^1.0.0":
|
||||
version: 1.0.3
|
||||
resolution: "gendocu-public-apis@npm:1.0.3"
|
||||
dependencies:
|
||||
"@improbable-eng/grpc-web": "npm:^0.14.0"
|
||||
google-protobuf: "npm:^3.15.8"
|
||||
checksum: 10/a34386a6137d92f392121305fb899f63ba1631a1b60c80781f6e56e160c1d462a8ce4b3ee93cb67c73c079b53c6de57ba19f514f29dd11c9783cd0d8227c6968
|
||||
checksum: 10/261a5cc97b599a7dfb492ac048e6bfce3f3bf64627f2b80ef195d5b6cefdbd4668d2b2ccd10676f01eb4f7862a5d19da567dd6ade9fc39a15268e81862308f7c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -42032,27 +42031,27 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"playwright-core@npm:1.53.0":
|
||||
version: 1.53.0
|
||||
resolution: "playwright-core@npm:1.53.0"
|
||||
"playwright-core@npm:1.53.1":
|
||||
version: 1.53.1
|
||||
resolution: "playwright-core@npm:1.53.1"
|
||||
bin:
|
||||
playwright-core: cli.js
|
||||
checksum: 10/881f27a9b7edd9954700489a5a4212cb91bcada226fd1d79a239b2eab0f333df1e2e41e275e6fa846d7f57c6a92afe14dca33ca7a2ce303dfb687d02511b7c69
|
||||
checksum: 10/d0ea8674c3abb76069255ca81bc0dfdef3f9548207f1404eec036bb8724135710f25ee791bfd7c043d5b9c2ccfa42288b0308d61dc5efc60a13b18811a15c4cd
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"playwright@npm:1.53.0":
|
||||
version: 1.53.0
|
||||
resolution: "playwright@npm:1.53.0"
|
||||
"playwright@npm:1.53.1":
|
||||
version: 1.53.1
|
||||
resolution: "playwright@npm:1.53.1"
|
||||
dependencies:
|
||||
fsevents: "npm:2.3.2"
|
||||
playwright-core: "npm:1.53.0"
|
||||
playwright-core: "npm:1.53.1"
|
||||
dependenciesMeta:
|
||||
fsevents:
|
||||
optional: true
|
||||
bin:
|
||||
playwright: cli.js
|
||||
checksum: 10/0b0258630f39b4d6ff1555d008ee4d591fe45cbe1e0f643a612397e3e6b1f7a99a2037a957eaa7351edd907ba10966ba105b2d244eafd1b247378910b660f086
|
||||
checksum: 10/74b3178d5ae3fde8de08fe6c221578530368f1abb8794fce234d06e0043178201eb3b7410354418517f23c105bd54ac1432da5f46c50353a5e6a80198a95f2cf
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in New Issue
Block a user