Merge branch 'backstage:master' into fix-gitlab-get-projectid
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/canon': patch
|
||||
---
|
||||
|
||||
Add min-width: 0; by default on every Flex components in Canon to help support truncated texts inside flex elements.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/canon': patch
|
||||
---
|
||||
|
||||
Fix styling for the title4 prop on the Heading component in Canon.
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-notifications': patch
|
||||
'@backstage/plugin-scaffolder': patch
|
||||
---
|
||||
|
||||
Remove React import form notification and scaffolder plugin
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/canon': patch
|
||||
---
|
||||
|
||||
Added a render prop to the Button component in Canon to use it as a link.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder': patch
|
||||
---
|
||||
|
||||
Added appropriate message when global templating function metadata is absent.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-home': patch
|
||||
---
|
||||
|
||||
Fixed the `WelcomeTitle` to properly default to the previous value of `inherit`
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/cli': patch
|
||||
---
|
||||
|
||||
Added `backstage.pluginId` field in `package.json` to all default plugin package templates for the `new` command.
|
||||
@@ -125,6 +125,20 @@ Here's a view when buttons are disabled.
|
||||
code={`<Button disabled>Button</Button>`}
|
||||
/>
|
||||
|
||||
### As Link
|
||||
|
||||
Here's a view when buttons are rendered as a link.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<ButtonSnippet story="AsLink" />}
|
||||
code={`<Button render={<a href="https://canon.backstage.io" target="_blank" />}>
|
||||
I am a link
|
||||
</Button>`}
|
||||
/>
|
||||
|
||||
### Responsive
|
||||
|
||||
Here's a view when buttons are responsive.
|
||||
|
||||
@@ -14,6 +14,12 @@ export const buttonPropDefs: Record<string, PropDef> = {
|
||||
default: 'medium',
|
||||
responsive: true,
|
||||
},
|
||||
render: {
|
||||
type: 'enum',
|
||||
values: ['ReactNode'],
|
||||
responsive: false,
|
||||
default: '<button />',
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
@@ -59,6 +59,7 @@ appearance of the heading.
|
||||
<Heading variant="title2">Title 2</Heading>
|
||||
<Heading variant="title3">Title 3</Heading>
|
||||
<Heading variant="title4">Title 4</Heading>
|
||||
<Heading variant="title5">Title 5</Heading>
|
||||
</Flex>`}
|
||||
/>
|
||||
|
||||
@@ -78,6 +79,17 @@ heading.
|
||||
</Heading>`}
|
||||
/>
|
||||
|
||||
### Custom render
|
||||
|
||||
You can also use the `render` prop to render the heading as a different element.
|
||||
|
||||
<Snippet
|
||||
py={2}
|
||||
open
|
||||
preview={<HeadingSnippet story="CustomRender" />}
|
||||
code={`<Heading render={<h4 />}>Custom render</Heading>`}
|
||||
/>
|
||||
|
||||
### Responsive
|
||||
|
||||
You can also use the `variant` prop to change the appearance of the text based
|
||||
|
||||
@@ -117,6 +117,16 @@ text.
|
||||
</Text>`}
|
||||
/>
|
||||
|
||||
### Custom render
|
||||
|
||||
You can also use the `render` prop to render the text as a different element.
|
||||
|
||||
<Snippet
|
||||
open
|
||||
preview={<TextSnippet story="CustomRender" />}
|
||||
code={`<Text render={<span />}>Custom render</Text>`}
|
||||
/>
|
||||
|
||||
### Responsive
|
||||
|
||||
You can also use the `variant` prop to change the appearance of the text based
|
||||
|
||||
@@ -8,8 +8,9 @@ description: Documentation for how to convert 3rd-party plugins to support the n
|
||||
|
||||
If you are using or want to use a 3rd-party plugin that does not yet support the new frontend system in your app, you can often use conversion utilities from `@backstage/core-compat-api` in order wrap the plugin to make it possible to install in your app.
|
||||
|
||||
> [!CAUTION]
|
||||
> The purpose of these utilities is to wrap 3rd-party plugins. Do not use them for your own plugins where you can add support for the new frontend system directly.
|
||||
:::caution
|
||||
The purpose of these utilities is to wrap 3rd-party plugins. Do not use them for your own plugins where you can add support for the new frontend system directly.
|
||||
:::
|
||||
|
||||
## Converting a legacy plugin
|
||||
|
||||
|
||||
@@ -186,6 +186,7 @@
|
||||
}
|
||||
|
||||
.canon-Flex {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@@ -515,7 +516,7 @@
|
||||
}
|
||||
|
||||
.canon-Heading[data-variant="title4"] {
|
||||
font-size: var(--canon-font-size-title4);
|
||||
font-size: var(--canon-font-size-6);
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
.canon-Flex {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
}
|
||||
|
||||
.canon-Heading[data-variant="title4"] {
|
||||
font-size: var(--canon-font-size-title4);
|
||||
font-size: var(--canon-font-size-6);
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
}
|
||||
|
||||
|
||||
@@ -9410,6 +9410,7 @@
|
||||
}
|
||||
|
||||
.canon-Flex {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@@ -9739,7 +9740,7 @@
|
||||
}
|
||||
|
||||
.canon-Heading[data-variant="title4"] {
|
||||
font-size: var(--canon-font-size-title4);
|
||||
font-size: var(--canon-font-size-6);
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
}
|
||||
|
||||
|
||||
@@ -149,7 +149,7 @@ export const breakpoints: Breakpoint[];
|
||||
|
||||
// @public (undocumented)
|
||||
export const Button: ForwardRefExoticComponent<
|
||||
ButtonProps & RefAttributes<HTMLButtonElement>
|
||||
Omit<ButtonProps, 'ref'> & RefAttributes<HTMLButtonElement>
|
||||
>;
|
||||
|
||||
// @public (undocumented)
|
||||
@@ -174,9 +174,7 @@ export const buttonPropDefs: {
|
||||
};
|
||||
|
||||
// @public
|
||||
export interface ButtonProps
|
||||
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {
|
||||
children: React.ReactNode;
|
||||
export interface ButtonProps extends useRender.ComponentProps<'button'> {
|
||||
iconEnd?: ReactElement;
|
||||
iconStart?: ReactElement;
|
||||
size?: ButtonOwnProps['size'];
|
||||
|
||||
@@ -131,6 +131,18 @@ export const Disabled: Story = {
|
||||
),
|
||||
};
|
||||
|
||||
export const AsLink: Story = {
|
||||
args: {
|
||||
children: 'I am a link',
|
||||
},
|
||||
render: args => (
|
||||
<Button
|
||||
{...args}
|
||||
render={<a href="https://canon.backstage.io" target="_blank" />}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
export const Responsive: Story = {
|
||||
args: {
|
||||
children: 'Button',
|
||||
|
||||
@@ -14,9 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { forwardRef } from 'react';
|
||||
import { forwardRef, useRef } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { useResponsiveValue } from '../../hooks/useResponsiveValue';
|
||||
import { useRender } from '@base-ui-components/react/use-render';
|
||||
|
||||
import type { ButtonProps } from './types';
|
||||
|
||||
@@ -26,10 +27,10 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
const {
|
||||
size = 'small',
|
||||
variant = 'primary',
|
||||
disabled,
|
||||
iconStart,
|
||||
iconEnd,
|
||||
children,
|
||||
render = <button />,
|
||||
className,
|
||||
style,
|
||||
...rest
|
||||
@@ -38,38 +39,43 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
// Get the responsive value for the variant
|
||||
const responsiveSize = useResponsiveValue(size);
|
||||
const responsiveVariant = useResponsiveValue(variant);
|
||||
const internalRef = useRef<HTMLElement | null>(null);
|
||||
|
||||
return (
|
||||
<button
|
||||
ref={ref}
|
||||
disabled={disabled}
|
||||
className={clsx('canon-Button', className)}
|
||||
data-size={responsiveSize}
|
||||
data-variant={responsiveVariant}
|
||||
style={style}
|
||||
{...rest}
|
||||
>
|
||||
{iconStart && (
|
||||
<span
|
||||
className="canon-ButtonIcon"
|
||||
aria-hidden="true"
|
||||
data-size={responsiveSize}
|
||||
>
|
||||
{iconStart}
|
||||
</span>
|
||||
)}
|
||||
{children}
|
||||
{iconEnd && (
|
||||
<span
|
||||
className="canon-ButtonIcon"
|
||||
aria-hidden="true"
|
||||
data-size={responsiveSize}
|
||||
>
|
||||
{iconEnd}
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
const { renderElement } = useRender({
|
||||
render,
|
||||
props: {
|
||||
className: clsx('canon-Button', className),
|
||||
['data-variant']: responsiveVariant,
|
||||
['data-size']: responsiveSize,
|
||||
...rest,
|
||||
children: (
|
||||
<>
|
||||
{iconStart && (
|
||||
<span
|
||||
className="canon-ButtonIcon"
|
||||
aria-hidden="true"
|
||||
data-size={responsiveSize}
|
||||
>
|
||||
{iconStart}
|
||||
</span>
|
||||
)}
|
||||
{children}
|
||||
{iconEnd && (
|
||||
<span
|
||||
className="canon-ButtonIcon"
|
||||
aria-hidden="true"
|
||||
data-size={responsiveSize}
|
||||
>
|
||||
{iconEnd}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
),
|
||||
},
|
||||
refs: [ref, internalRef],
|
||||
});
|
||||
|
||||
return renderElement();
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -16,14 +16,14 @@
|
||||
|
||||
import type { ButtonOwnProps } from './Button.props';
|
||||
import { ReactElement } from 'react';
|
||||
import type { useRender } from '@base-ui-components/react/use-render';
|
||||
|
||||
/**
|
||||
* Properties for {@link Button}
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface ButtonProps
|
||||
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {
|
||||
export interface ButtonProps extends useRender.ComponentProps<'button'> {
|
||||
/**
|
||||
* The size of the button
|
||||
* @defaultValue 'medium'
|
||||
@@ -36,11 +36,6 @@ export interface ButtonProps
|
||||
*/
|
||||
variant?: ButtonOwnProps['variant'];
|
||||
|
||||
/**
|
||||
* The content of the button
|
||||
*/
|
||||
children: React.ReactNode;
|
||||
|
||||
/**
|
||||
* Optional icon to display at the start of the button
|
||||
*/
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { Flex } from './Flex';
|
||||
import { Text } from '../Text';
|
||||
|
||||
const meta = {
|
||||
title: 'Layout/Flex',
|
||||
@@ -143,3 +144,24 @@ export const LargeGap: Story = {
|
||||
</Flex>
|
||||
),
|
||||
};
|
||||
|
||||
export const WithTextTruncate: Story = {
|
||||
render: () => (
|
||||
<Flex direction="row" gap="8">
|
||||
<Flex>
|
||||
<Text truncate>
|
||||
A man looks at a painting in a museum and says, “Brothers and sisters
|
||||
I have none, but that man's father is my father's son.” Who
|
||||
is in the painting?
|
||||
</Text>
|
||||
</Flex>
|
||||
<Flex>
|
||||
<Text truncate>
|
||||
A man looks at a painting in a museum and says, “Brothers and sisters
|
||||
I have none, but that man's father is my father's son.” Who
|
||||
is in the painting?
|
||||
</Text>
|
||||
</Flex>
|
||||
</Flex>
|
||||
),
|
||||
};
|
||||
|
||||
@@ -16,4 +16,7 @@
|
||||
|
||||
.canon-Flex {
|
||||
display: flex;
|
||||
|
||||
/* This helps when using `truncate` on text inside a flex container */
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ export const AllVariants: Story = {
|
||||
<Heading variant="title2">Title 2</Heading>
|
||||
<Heading variant="title3">Title 3</Heading>
|
||||
<Heading variant="title4">Title 4</Heading>
|
||||
<Heading variant="title5">Title 5</Heading>
|
||||
</Flex>
|
||||
),
|
||||
};
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
}
|
||||
|
||||
.canon-Heading[data-variant='title4'] {
|
||||
font-size: var(--canon-font-size-title4);
|
||||
font-size: var(--canon-font-size-6);
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"backstage": {
|
||||
"role": "backend-plugin-module"
|
||||
"role": "backend-plugin-module",
|
||||
"pluginId": "{{pluginId}}"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "backstage-cli package start",
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"backstage": {
|
||||
"role": "backend-plugin"
|
||||
"role": "backend-plugin",
|
||||
"pluginId": "{{pluginId}}"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "backstage-cli package start",
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"backstage": {
|
||||
"role": "frontend-plugin"
|
||||
"role": "frontend-plugin",
|
||||
"pluginId": "{{pluginId}}"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"scripts": {
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"backstage": {
|
||||
"role": "common-library"
|
||||
"role": "common-library",
|
||||
"pluginId": "{{pluginId}}"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"scripts": {
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"backstage": {
|
||||
"role": "node-library"
|
||||
"role": "node-library",
|
||||
"pluginId": "{{pluginId}}"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "backstage-cli package build",
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"backstage": {
|
||||
"role": "web-library"
|
||||
"role": "web-library",
|
||||
"pluginId": "{{pluginId}}"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"scripts": {
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"backstage": {
|
||||
"role": "backend-plugin-module"
|
||||
"role": "backend-plugin-module",
|
||||
"pluginId": "scaffolder"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "backstage-cli package start",
|
||||
|
||||
@@ -33,7 +33,7 @@ export type WelcomeTitleLanguageProps = {
|
||||
|
||||
export const WelcomeTitle = ({
|
||||
language,
|
||||
variant,
|
||||
variant = 'inherit',
|
||||
}: WelcomeTitleLanguageProps) => {
|
||||
const identityApi = useApi(identityApiRef);
|
||||
const alertApi = useApi(alertApiRef);
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
ChannelSetting,
|
||||
isNotificationsEnabledFor,
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
isNotificationsEnabledFor,
|
||||
NotificationSettings,
|
||||
|
||||
@@ -430,6 +430,7 @@ export const scaffolderTranslationRef: TranslationRef<
|
||||
readonly 'templatingExtensions.content.functions.schema.arguments': 'Arguments';
|
||||
readonly 'templatingExtensions.content.functions.examples': 'Examples';
|
||||
readonly 'templatingExtensions.content.functions.notAvailable': 'There are no global template functions defined.';
|
||||
readonly 'templatingExtensions.content.functions.metadataAbsent': 'Function metadata unavailable';
|
||||
readonly 'templatingExtensions.title': 'Templating Extensions';
|
||||
readonly 'templatingExtensions.subtitle': 'This is the collection of available templating extensions';
|
||||
readonly 'templatingExtensions.pageTitle': 'Templating Extensions';
|
||||
|
||||
@@ -26,7 +26,7 @@ import { ClassNameMap } from '@material-ui/core/styles/withStyles';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
|
||||
import classNames from 'classnames';
|
||||
import React, { ReactElement, useState } from 'react';
|
||||
import { cloneElement, Fragment, ReactElement, useState } from 'react';
|
||||
import { scaffolderTranslationRef } from '../../translation';
|
||||
import { Expanded, RenderSchema, SchemaRenderContext } from '../RenderSchema';
|
||||
import { ScaffolderUsageExamplesTable } from '../ScaffolderUsageExamplesTable';
|
||||
@@ -60,7 +60,7 @@ const FilterDetailContent = ({
|
||||
headings: [<Typography variant="h6" component="h4" />],
|
||||
};
|
||||
return (
|
||||
<React.Fragment key={`${name}.detail`}>
|
||||
<Fragment key={`${name}.detail`}>
|
||||
{filter.description && <MarkdownContent content={filter.description} />}
|
||||
<Box pb={2}>
|
||||
<Typography variant="h5" component="h3">
|
||||
@@ -84,7 +84,7 @@ const FilterDetailContent = ({
|
||||
const [argSchema, required] = inspectFunctionArgSchema(arg);
|
||||
|
||||
return (
|
||||
<React.Fragment key={i}>
|
||||
<Fragment key={i}>
|
||||
<div
|
||||
className={classNames({ [classes.argRequired]: required })}
|
||||
>
|
||||
@@ -101,7 +101,7 @@ const FilterDetailContent = ({
|
||||
}}
|
||||
schema={argSchema}
|
||||
/>
|
||||
</React.Fragment>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
@@ -133,7 +133,7 @@ const FilterDetailContent = ({
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
)}
|
||||
</React.Fragment>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -178,7 +178,7 @@ export const TemplateFilters = ({
|
||||
>
|
||||
{name}
|
||||
</Typography>
|
||||
{React.cloneElement(baseLink, {
|
||||
{cloneElement(baseLink, {
|
||||
to: `${baseLink.props.to}#${fragment}`,
|
||||
})}
|
||||
<FilterDetailContent {...{ t, classes, name, filter }} />
|
||||
|
||||
@@ -26,7 +26,7 @@ import { ClassNameMap } from '@material-ui/core/styles/withStyles';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
|
||||
import classNames from 'classnames';
|
||||
import React, { ReactElement, useState } from 'react';
|
||||
import { cloneElement, Fragment, ReactElement, useState } from 'react';
|
||||
import { scaffolderTranslationRef } from '../../translation';
|
||||
import { Expanded, RenderSchema, SchemaRenderContext } from '../RenderSchema';
|
||||
import { ScaffolderUsageExamplesTable } from '../ScaffolderUsageExamplesTable';
|
||||
@@ -48,8 +48,11 @@ const FunctionDetailContent = ({
|
||||
const expanded = useState<Expanded>({});
|
||||
if (!Object.keys(fn).length) {
|
||||
return (
|
||||
<Typography style={{ fontStyle: 'italic' }}>
|
||||
{t('templatingExtensions.content.functions.notAvailable')}
|
||||
<Typography
|
||||
style={{ fontStyle: 'italic' }}
|
||||
data-testid={`${name}.metadataAbsent`}
|
||||
>
|
||||
{t('templatingExtensions.content.functions.metadataAbsent')}
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
@@ -60,7 +63,7 @@ const FunctionDetailContent = ({
|
||||
headings: [<Typography variant="h6" component="h4" />],
|
||||
};
|
||||
return (
|
||||
<React.Fragment key={`${name}.detail`}>
|
||||
<Fragment key={`${name}.detail`}>
|
||||
{fn.description && <MarkdownContent content={fn.description} />}
|
||||
{schema?.arguments?.length && (
|
||||
<Box key={`${name}.args`} pb={2}>
|
||||
@@ -71,7 +74,7 @@ const FunctionDetailContent = ({
|
||||
const [argSchema, required] = inspectFunctionArgSchema(arg);
|
||||
|
||||
return (
|
||||
<React.Fragment key={i}>
|
||||
<Fragment key={i}>
|
||||
<div
|
||||
className={classNames({ [classes.argRequired]: required })}
|
||||
>
|
||||
@@ -89,7 +92,7 @@ const FunctionDetailContent = ({
|
||||
}}
|
||||
schema={argSchema}
|
||||
/>
|
||||
</React.Fragment>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
@@ -121,7 +124,7 @@ const FunctionDetailContent = ({
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
)}
|
||||
</React.Fragment>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -166,7 +169,7 @@ export const TemplateGlobalFunctions = ({
|
||||
>
|
||||
{name}
|
||||
</Typography>
|
||||
{React.cloneElement(baseLink, {
|
||||
{cloneElement(baseLink, {
|
||||
to: `${baseLink.props.to}#${fragment}`,
|
||||
})}
|
||||
<FunctionDetailContent {...{ classes, name, fn, t }} />
|
||||
@@ -218,7 +221,7 @@ export const TemplateGlobalValues = ({
|
||||
>
|
||||
{name}
|
||||
</Typography>
|
||||
{React.cloneElement(baseLink, {
|
||||
{cloneElement(baseLink, {
|
||||
to: `${baseLink.props.to}#${fragment}`,
|
||||
})}
|
||||
{gv.description && <MarkdownContent content={gv.description} />}
|
||||
|
||||
+95
-59
@@ -315,76 +315,112 @@ describe('TemplatingExtensionsPage', () => {
|
||||
});
|
||||
});
|
||||
describe('renders global', () => {
|
||||
it('renders global functions', async () => {
|
||||
listTemplatingExtensions.mockResolvedValue({
|
||||
...emptyExtensions,
|
||||
globals: {
|
||||
...emptyExtensions.globals,
|
||||
functions: {
|
||||
truthy: {
|
||||
description: 'evaluate truthiness',
|
||||
schema: {
|
||||
arguments: [
|
||||
{
|
||||
title: 'input',
|
||||
},
|
||||
],
|
||||
output: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
examples: [
|
||||
{
|
||||
description: 'basic usage',
|
||||
example: "truthy('foo')",
|
||||
notes: 'yields `true`',
|
||||
},
|
||||
],
|
||||
describe('renders global functions', () => {
|
||||
it('without metadata', async () => {
|
||||
listTemplatingExtensions.mockResolvedValue({
|
||||
...emptyExtensions,
|
||||
globals: {
|
||||
...emptyExtensions.globals,
|
||||
functions: {
|
||||
anything: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const { findByTestId, getByRole } = await r();
|
||||
|
||||
fireEvent.click(within(getByRole('tablist')).getByText('Functions'));
|
||||
|
||||
const functions = await findByTestId('functions');
|
||||
|
||||
const anything = within(functions).getByTestId('anything');
|
||||
const title = within(anything).getByText('anything');
|
||||
expect(title).toBeInTheDocument();
|
||||
expect(title.id).toBe('function_anything');
|
||||
|
||||
const link = within(anything).getByRole('link');
|
||||
expect(link).toBeInTheDocument();
|
||||
expect(link).toHaveAttribute(
|
||||
'href',
|
||||
expect.stringMatching(new RegExp(`#${title.id}$`)),
|
||||
);
|
||||
expect(
|
||||
within(anything).getByTestId('anything.metadataAbsent'),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
const { findByTestId, getByRole } = await r();
|
||||
it('with metadata', async () => {
|
||||
listTemplatingExtensions.mockResolvedValue({
|
||||
...emptyExtensions,
|
||||
globals: {
|
||||
...emptyExtensions.globals,
|
||||
functions: {
|
||||
truthy: {
|
||||
description: 'evaluate truthiness',
|
||||
schema: {
|
||||
arguments: [
|
||||
{
|
||||
title: 'input',
|
||||
},
|
||||
],
|
||||
output: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
examples: [
|
||||
{
|
||||
description: 'basic usage',
|
||||
example: "truthy('foo')",
|
||||
notes: 'yields `true`',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const { findByTestId, getByRole } = await r();
|
||||
|
||||
fireEvent.click(within(getByRole('tablist')).getByText('Functions'));
|
||||
fireEvent.click(within(getByRole('tablist')).getByText('Functions'));
|
||||
|
||||
const functions = await findByTestId('functions');
|
||||
const functions = await findByTestId('functions');
|
||||
|
||||
const truthy = within(functions).getByTestId('truthy');
|
||||
const title = within(truthy).getByText('truthy');
|
||||
expect(title).toBeInTheDocument();
|
||||
expect(title.id).toBe('function_truthy');
|
||||
const truthy = within(functions).getByTestId('truthy');
|
||||
const title = within(truthy).getByText('truthy');
|
||||
expect(title).toBeInTheDocument();
|
||||
expect(title.id).toBe('function_truthy');
|
||||
|
||||
const link = within(truthy).getByRole('link');
|
||||
expect(link).toBeInTheDocument();
|
||||
expect(link).toHaveAttribute(
|
||||
'href',
|
||||
expect.stringMatching(new RegExp(`#${title.id}$`)),
|
||||
);
|
||||
const link = within(truthy).getByRole('link');
|
||||
expect(link).toBeInTheDocument();
|
||||
expect(link).toHaveAttribute(
|
||||
'href',
|
||||
expect.stringMatching(new RegExp(`#${title.id}$`)),
|
||||
);
|
||||
expect(
|
||||
within(truthy).queryByTestId('truthy.metadataAbsent'),
|
||||
).not.toBeInTheDocument();
|
||||
|
||||
expect(
|
||||
within(truthy).getByText('evaluate truthiness'),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
within(truthy).getByText('evaluate truthiness'),
|
||||
).toBeInTheDocument();
|
||||
|
||||
expect(within(truthy).getByText('[0]')).toBeInTheDocument();
|
||||
expect(
|
||||
within(truthy).getByTestId('root_truthy.arg0'),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
within(truthy).queryByTestId('root_truthy.arg1'),
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
within(truthy).getByTestId('root_truthy.output'),
|
||||
).toBeInTheDocument();
|
||||
expect(within(truthy).getByText('[0]')).toBeInTheDocument();
|
||||
expect(
|
||||
within(truthy).getByTestId('root_truthy.arg0'),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
within(truthy).queryByTestId('root_truthy.arg1'),
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
within(truthy).getByTestId('root_truthy.output'),
|
||||
).toBeInTheDocument();
|
||||
|
||||
const x = within(truthy).getByTestId('examples');
|
||||
expect(x).toBeInTheDocument();
|
||||
const xd0 = within(x).getByTestId('example_desc0');
|
||||
expect(xd0).toBeInTheDocument();
|
||||
expect(xd0).toHaveTextContent(/basic usage\s*yields\s*true/);
|
||||
const x = within(truthy).getByTestId('examples');
|
||||
expect(x).toBeInTheDocument();
|
||||
const xd0 = within(x).getByTestId('example_desc0');
|
||||
expect(xd0).toBeInTheDocument();
|
||||
expect(xd0).toHaveTextContent(/basic usage\s*yields\s*true/);
|
||||
|
||||
const xc0 = within(x).getByTestId('example_code0');
|
||||
expect(within(xc0).getByText("truthy('foo')")).toBeInTheDocument();
|
||||
const xc0 = within(x).getByTestId('example_code0');
|
||||
expect(within(xc0).getByText("truthy('foo')")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
it('renders global values', async () => {
|
||||
const msvValue = ['foo', 'bar', 'baz'];
|
||||
|
||||
@@ -226,6 +226,7 @@ export const scaffolderTranslationRef = createTranslationRef({
|
||||
functions: {
|
||||
title: 'Functions',
|
||||
notAvailable: 'There are no global template functions defined.',
|
||||
metadataAbsent: 'Function metadata unavailable',
|
||||
schema: {
|
||||
arguments: 'Arguments',
|
||||
output: 'Output',
|
||||
|
||||
Reference in New Issue
Block a user