mirror of
https://github.com/gravitational/teleport.git
synced 2026-11-03 16:38:35 +08:00
* Use event.code for non A-z keys Without this fix, shortcuts like "Shift+1" can't work, because the reported character is "!". * Add Zod schema to validate keyboard shortcuts * Render better formatted error notification, add a link to docs * Notify about duplicated accelerators * Improve the error message for re-parsing failure (it can happen when a default value does not pass validation) * A bunch of renames * Allow spaces in accelerators * Rename `isContentAnObject` * Allow `Notification` to render links and lists * Split by optional whitespace and "+" * Allow lowercase letters * Require a modifier for non-function keys * Move VALID_SHORTCUT_MESSAGE to `initUi` * Always return from `getDuplicateAccelerators` * Use 'Cmd' and 'Ctrl' for mac * Add comments * Fix notification not rendering content * Add more comments, rename `getKeyCode` to `getKeyName` * Fix incorrect size of <Link> text * Remove "expected" prefix * Revert `typeof content === 'object'` in `Notification` * Remove a comment about disabled keys in `ConfigService`, add a note about `keymap.` prefix * Improve `getKeyName` comment * Extract an inline object to a variable in `getKeyName` * Fix notification list padding * Change text for doc link & description for config error * Improve comment for `getKeyName` * Remove special formatting for list === 1 in `Notification` * Print valid modifiers * Call `getKeyboardShortcutSchema()` once * Collect issues from all validations, run `invalidModifiers` through a set * Change error message for `missingModifierIssue` * Convert duplicates warning to error * Define ALLOWED_KEY_CODES in a more concise way * Support both `IntlBackslash` and `Backquote` * Restore modifiers for mac to full spelling, sort them in order recommended by platform guidelines * Fix old comment about the shortcuts order --------- Co-authored-by: Rafał Cieślak <rafal.cieslak@goteleport.com>
326 lines
9.4 KiB
TypeScript
326 lines
9.4 KiB
TypeScript
/*
|
|
Copyright 2022 Gravitational, Inc.
|
|
|
|
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 React, { useState } from 'react';
|
|
import { Info, Warning } from 'design/Icon';
|
|
import Flex from 'design/Flex';
|
|
|
|
import { Notification } from './Notification';
|
|
|
|
export default {
|
|
title: 'Shared/Notification',
|
|
};
|
|
|
|
export const Notifications = () => {
|
|
return (
|
|
<div
|
|
css={`
|
|
display: grid;
|
|
grid-gap: ${props => props.theme.space[8]}px;
|
|
grid-template-columns: auto auto auto;
|
|
`}
|
|
>
|
|
<Flex flexDirection="column" gap={4}>
|
|
<Notification
|
|
item={{
|
|
id: crypto.randomUUID(),
|
|
severity: 'info',
|
|
content: {
|
|
title: 'Info with title and description',
|
|
description: loremIpsum,
|
|
},
|
|
}}
|
|
Icon={Info}
|
|
getColor={theme => theme.colors.info}
|
|
onRemove={() => {}}
|
|
isAutoRemovable={false}
|
|
/>
|
|
|
|
<Notification
|
|
item={{
|
|
id: crypto.randomUUID(),
|
|
severity: 'warn',
|
|
content: {
|
|
title: 'Warning with title and description',
|
|
description: loremIpsum,
|
|
},
|
|
}}
|
|
Icon={Warning}
|
|
getColor={theme => theme.colors.warning}
|
|
onRemove={() => {}}
|
|
isAutoRemovable={false}
|
|
/>
|
|
|
|
<Notification
|
|
item={{
|
|
id: crypto.randomUUID(),
|
|
severity: 'error',
|
|
content: {
|
|
title: 'Error with title and description',
|
|
description: loremIpsum,
|
|
},
|
|
}}
|
|
Icon={Warning}
|
|
getColor={theme => theme.colors.danger}
|
|
onRemove={() => {}}
|
|
isAutoRemovable={false}
|
|
/>
|
|
</Flex>
|
|
|
|
<Flex flexDirection="column" gap={4}>
|
|
<Notification
|
|
item={{
|
|
id: crypto.randomUUID(),
|
|
severity: 'info',
|
|
content: 'Multiline info without title. ' + loremIpsum,
|
|
}}
|
|
Icon={Info}
|
|
getColor={theme => theme.colors.info}
|
|
onRemove={() => {}}
|
|
isAutoRemovable={false}
|
|
/>
|
|
|
|
<Notification
|
|
item={{
|
|
id: crypto.randomUUID(),
|
|
severity: 'warn',
|
|
content: 'Multiline warning without title. ' + loremIpsum,
|
|
}}
|
|
Icon={Warning}
|
|
getColor={theme => theme.colors.warning}
|
|
onRemove={() => {}}
|
|
isAutoRemovable={false}
|
|
/>
|
|
|
|
<Notification
|
|
item={{
|
|
id: crypto.randomUUID(),
|
|
severity: 'error',
|
|
content: 'Multiline error without title. ' + loremIpsum,
|
|
}}
|
|
Icon={Warning}
|
|
getColor={theme => theme.colors.danger}
|
|
onRemove={() => {}}
|
|
isAutoRemovable={false}
|
|
/>
|
|
</Flex>
|
|
|
|
<Flex flexDirection="column" gap={4}>
|
|
<Notification
|
|
item={{
|
|
id: crypto.randomUUID(),
|
|
severity: 'info',
|
|
content: 'Info without title',
|
|
}}
|
|
Icon={Info}
|
|
getColor={theme => theme.colors.info}
|
|
onRemove={() => {}}
|
|
isAutoRemovable={false}
|
|
/>
|
|
|
|
<Notification
|
|
item={{
|
|
id: crypto.randomUUID(),
|
|
severity: 'warn',
|
|
content: 'Warning without title',
|
|
}}
|
|
Icon={Warning}
|
|
getColor={theme => theme.colors.warning}
|
|
onRemove={() => {}}
|
|
isAutoRemovable={false}
|
|
/>
|
|
|
|
<Notification
|
|
item={{
|
|
id: crypto.randomUUID(),
|
|
severity: 'error',
|
|
content: 'Error without title',
|
|
}}
|
|
Icon={Warning}
|
|
getColor={theme => theme.colors.danger}
|
|
onRemove={() => {}}
|
|
isAutoRemovable={false}
|
|
/>
|
|
</Flex>
|
|
|
|
<Flex flexDirection="column" gap={4}>
|
|
<Notification
|
|
item={{
|
|
id: crypto.randomUUID(),
|
|
severity: 'info',
|
|
content: {
|
|
title: 'Info with link',
|
|
description: loremIpsum,
|
|
link: {
|
|
href: 'https://goteleport.com',
|
|
text: 'goteleport.com',
|
|
},
|
|
},
|
|
}}
|
|
Icon={Info}
|
|
getColor={theme => theme.colors.info}
|
|
onRemove={() => {}}
|
|
isAutoRemovable={false}
|
|
/>
|
|
<Notification
|
|
item={{
|
|
id: crypto.randomUUID(),
|
|
severity: 'warn',
|
|
content: {
|
|
title: 'Warning with link',
|
|
description: loremIpsum,
|
|
link: {
|
|
href: 'https://goteleport.com',
|
|
text: 'goteleport.com',
|
|
},
|
|
},
|
|
}}
|
|
Icon={Warning}
|
|
getColor={theme => theme.colors.warning}
|
|
onRemove={() => {}}
|
|
isAutoRemovable={false}
|
|
/>
|
|
<Notification
|
|
item={{
|
|
id: crypto.randomUUID(),
|
|
severity: 'error',
|
|
content: {
|
|
title: 'Error with link',
|
|
description: loremIpsum,
|
|
link: {
|
|
href: 'https://goteleport.com',
|
|
text: 'goteleport.com',
|
|
},
|
|
},
|
|
}}
|
|
Icon={Warning}
|
|
getColor={theme => theme.colors.danger}
|
|
onRemove={() => {}}
|
|
isAutoRemovable={false}
|
|
/>
|
|
</Flex>
|
|
|
|
<Flex flexDirection="column" gap={4}>
|
|
<Notification
|
|
item={{
|
|
id: crypto.randomUUID(),
|
|
severity: 'info',
|
|
content: {
|
|
title: 'Info with list',
|
|
list: [loremIpsum, loremIpsum],
|
|
},
|
|
}}
|
|
Icon={Info}
|
|
getColor={theme => theme.colors.info}
|
|
onRemove={() => {}}
|
|
isAutoRemovable={false}
|
|
/>
|
|
<Notification
|
|
item={{
|
|
id: crypto.randomUUID(),
|
|
severity: 'warn',
|
|
content: {
|
|
title: 'Warning with list',
|
|
list: [loremIpsum, loremIpsum],
|
|
},
|
|
}}
|
|
Icon={Warning}
|
|
getColor={theme => theme.colors.warning}
|
|
onRemove={() => {}}
|
|
isAutoRemovable={false}
|
|
/>
|
|
<Notification
|
|
item={{
|
|
id: crypto.randomUUID(),
|
|
severity: 'error',
|
|
content: {
|
|
title: 'Error with list',
|
|
list: [loremIpsum, loremIpsum],
|
|
},
|
|
}}
|
|
Icon={Warning}
|
|
getColor={theme => theme.colors.danger}
|
|
onRemove={() => {}}
|
|
isAutoRemovable={false}
|
|
/>
|
|
</Flex>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export const AutoRemovable = () => {
|
|
const [showInfo, setShowInfo] = useState(true);
|
|
const [showWarning, setShowWarning] = useState(true);
|
|
const [showError, setShowError] = useState(true);
|
|
return (
|
|
<Flex flexDirection="column" gap={4}>
|
|
{showInfo ? (
|
|
<Notification
|
|
item={{
|
|
id: crypto.randomUUID(),
|
|
severity: 'info',
|
|
content:
|
|
"This will be automatically removed after 5 seconds. Click to expand it. Mouseover it to restart the timer. Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s.",
|
|
}}
|
|
onRemove={() => setShowInfo(false)}
|
|
Icon={Info}
|
|
getColor={theme => theme.colors.info}
|
|
isAutoRemovable={true}
|
|
autoRemoveDurationMs={5000}
|
|
/>
|
|
) : (
|
|
<div>Info notification has been removed</div>
|
|
)}
|
|
{showWarning ? (
|
|
<Notification
|
|
item={{
|
|
id: crypto.randomUUID(),
|
|
severity: 'warn',
|
|
content:
|
|
"This will be automatically removed after 5 seconds. Click to expand it. Mouseover it to restart the timer. Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s.",
|
|
}}
|
|
onRemove={() => setShowWarning(false)}
|
|
Icon={Warning}
|
|
getColor={theme => theme.colors.warning}
|
|
isAutoRemovable={true}
|
|
autoRemoveDurationMs={5000}
|
|
/>
|
|
) : (
|
|
<div>Warning notification has been removed</div>
|
|
)}
|
|
{showError ? (
|
|
<Notification
|
|
item={{
|
|
id: crypto.randomUUID(),
|
|
severity: 'error',
|
|
content:
|
|
"This can only be removed by clicking on the X. Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s.",
|
|
}}
|
|
onRemove={() => setShowError(false)}
|
|
Icon={Warning}
|
|
getColor={theme => theme.colors.danger}
|
|
isAutoRemovable={false}
|
|
/>
|
|
) : (
|
|
<div>Error notification has been removed</div>
|
|
)}
|
|
</Flex>
|
|
);
|
|
};
|
|
|
|
const loremIpsum =
|
|
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut non ipsum dignissim, dignissim est vitae, facilisis nunc.';
|