mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[SharedUX/SCSS] remove scss from selected components (#207008)
## Summary Part of https://github.com/elastic/kibana-team/issues/1417 This PR converts a batch of SCSS to Emotion in the SharedUX domain. * `KibanaSolutionAvatar` * `FilePicker` * `SolutionNav` * `SolutionNavCollapseButton` * `KbnTopNav` All of the changes, except for `KbnTopNav`, can be tested in Storybook by running `yarn storybook shared_ux` ### Checklist Check the PR satisfies following conditions. Reviewers should verify this PR satisfies this list as well. - [x] The PR description includes the appropriate Release Notes section, and the correct `release_note:*` label is applied per the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) ### Identify risks Does this PR introduce any risks? For example, consider risks like hard to test bugs, performance regression, potential of data loss. Describe the risk, its severity, and mitigation for each identified risk. Invite stakeholders and evaluate how to proceed before merging. - [x] The risk of inexact conversion: verifying this PR requires manual checks to ensure that the conversion has not created any regressions in the style.
This commit is contained in:
parent
02a2e054d8
commit
06a28ae4f7
29 changed files with 436 additions and 266 deletions
|
@ -7,8 +7,6 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import './index.scss';
|
||||
|
||||
import { ScreenshotModeExamplePlugin } from './plugin';
|
||||
|
||||
// This exports static code and TypeScript types,
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"type": "shared-common",
|
||||
"type": "shared-browser",
|
||||
"id": "@kbn/shared-ux-avatar-solution",
|
||||
"owner": [
|
||||
"@elastic/appex-sharedux"
|
||||
],
|
||||
"group": "platform",
|
||||
"visibility": "shared"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,24 @@
|
|||
|
||||
exports[`KibanaSolutionAvatar renders 1`] = `
|
||||
<EuiAvatar
|
||||
className="kbnSolutionAvatar"
|
||||
color="plain"
|
||||
css={
|
||||
Array [
|
||||
Object {
|
||||
"map": undefined,
|
||||
"name": "ym1nj7",
|
||||
"next": undefined,
|
||||
"styles": "
|
||||
box-shadow:
|
||||
0 .7px 1.4px rgba(0,0,0,0.07),
|
||||
0 1.9px 4px rgba(0,0,0,0.05),
|
||||
0 4.5px 10px rgba(0,0,0,0.05);
|
||||
",
|
||||
"toString": [Function],
|
||||
},
|
||||
false,
|
||||
]
|
||||
}
|
||||
iconType="logoElastic"
|
||||
name="Solution"
|
||||
/>
|
||||
|
@ -11,8 +27,24 @@ exports[`KibanaSolutionAvatar renders 1`] = `
|
|||
|
||||
exports[`KibanaSolutionAvatar renders 2`] = `
|
||||
<EuiAvatar
|
||||
className="kbnSolutionAvatar"
|
||||
color="plain"
|
||||
css={
|
||||
Array [
|
||||
Object {
|
||||
"map": undefined,
|
||||
"name": "ym1nj7",
|
||||
"next": undefined,
|
||||
"styles": "
|
||||
box-shadow:
|
||||
0 .7px 1.4px rgba(0,0,0,0.07),
|
||||
0 1.9px 4px rgba(0,0,0,0.05),
|
||||
0 4.5px 10px rgba(0,0,0,0.05);
|
||||
",
|
||||
"toString": [Function],
|
||||
},
|
||||
false,
|
||||
]
|
||||
}
|
||||
iconType="logoElasticStack"
|
||||
name="Elastic Stack"
|
||||
/>
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
.kbnSolutionAvatar {
|
||||
@include euiBottomShadowSmall;
|
||||
|
||||
&--xxl {
|
||||
@include euiBottomShadowMedium;
|
||||
@include size(100px);
|
||||
line-height: 100px;
|
||||
border-radius: 100px;
|
||||
display: inline-block;
|
||||
background: $euiColorEmptyShade url('assets/texture.svg') no-repeat;
|
||||
background-size: cover, 125%;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
|
@ -7,15 +7,22 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import './solution_avatar.scss';
|
||||
|
||||
import { css } from '@emotion/react';
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { DistributiveOmit, EuiAvatar, EuiAvatarProps, IconType } from '@elastic/eui';
|
||||
import {
|
||||
DistributiveOmit,
|
||||
EuiAvatar,
|
||||
EuiAvatarProps,
|
||||
IconType,
|
||||
useEuiShadow,
|
||||
useEuiTheme,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { SolutionNameType } from './types';
|
||||
|
||||
import textureImage from './assets/texture.svg';
|
||||
|
||||
export type KnownSolutionProps = DistributiveOmit<EuiAvatarProps, 'size' | 'name' | 'iconType'> & {
|
||||
/**
|
||||
* Any EuiAvatar size available, or `xxl` for custom large, brand-focused version
|
||||
|
@ -56,16 +63,27 @@ export const KibanaSolutionAvatar = (props: KibanaSolutionAvatarProps) => {
|
|||
icon.iconType = `logo${props.name.replace(/\s+/g, '')}`;
|
||||
}
|
||||
|
||||
const { euiTheme } = useEuiTheme();
|
||||
const styles = {
|
||||
base: css(useEuiShadow('s')),
|
||||
xxl: css`
|
||||
${useEuiShadow('m')};
|
||||
line-height: calc(${euiTheme.size.xs} * 25);
|
||||
width: calc(${euiTheme.size.xs} * 25);
|
||||
height: calc(${euiTheme.size.xs} * 25);
|
||||
border-radius: calc(${euiTheme.size.xs} * 25);
|
||||
display: inline-block;
|
||||
background: ${euiTheme.colors.backgroundBasePlain} url(${textureImage}) no-repeat;
|
||||
background-size: cover, 125%;
|
||||
text-align: center;
|
||||
`,
|
||||
};
|
||||
|
||||
return (
|
||||
// @ts-ignore Complains about ExclusiveUnion between `iconSize` and `iconType`, but works fine
|
||||
<EuiAvatar
|
||||
className={classNames(
|
||||
'kbnSolutionAvatar',
|
||||
{
|
||||
[`kbnSolutionAvatar--${size}`]: size,
|
||||
},
|
||||
className
|
||||
)}
|
||||
css={[styles.base, size === 'xxl' && styles.xxl]}
|
||||
className={className}
|
||||
size={size === 'xxl' ? 'xl' : size}
|
||||
iconSize={size}
|
||||
color="plain"
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
.filesFilePicker {
|
||||
.euiCard__content, .euiCard__description {
|
||||
margin :0; // make the cards a little bit more compact
|
||||
}
|
||||
}
|
|
@ -20,8 +20,6 @@ import type { FileImageMetadata } from '@kbn/shared-ux-file-types';
|
|||
import { useFilePickerContext } from '../context';
|
||||
import { i18nTexts } from '../i18n_texts';
|
||||
|
||||
import './file_card.scss';
|
||||
|
||||
interface Props {
|
||||
file: FileJSON;
|
||||
}
|
||||
|
|
|
@ -92,7 +92,6 @@ const Component: FunctionComponent<InnerProps> = ({ onClose, onDone, onUpload, m
|
|||
const modal = (
|
||||
<EuiModal
|
||||
data-test-subj="filePickerModal"
|
||||
className="filesFilePicker filesFilePicker--fixed"
|
||||
maxWidth="75vw"
|
||||
onClose={onClose}
|
||||
css={css`
|
||||
|
@ -100,6 +99,10 @@ const Component: FunctionComponent<InnerProps> = ({ onClose, onDone, onUpload, m
|
|||
width: 75vw;
|
||||
height: 75vh;
|
||||
}
|
||||
.euiCard__content,
|
||||
.euiCard__description {
|
||||
margin: 0; // make the cards a little bit more compact
|
||||
}
|
||||
`}
|
||||
>
|
||||
<EuiModalHeader>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"type": "shared-common",
|
||||
"type": "shared-browser",
|
||||
"id": "@kbn/shared-ux-page-solution-nav",
|
||||
"owner": [
|
||||
"@elastic/appex-sharedux"
|
||||
],
|
||||
"group": "platform",
|
||||
"visibility": "shared"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`SolutionNavCollapseButton isCollapsed 1`] = `
|
||||
<EuiButtonIcon
|
||||
aria-label="Open side navigation"
|
||||
className="kbnSolutionNavCollapseButton kbnSolutionNavCollapseButton-isCollapsed"
|
||||
color="text"
|
||||
iconType="menuRight"
|
||||
size="s"
|
||||
title="Open side navigation"
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`SolutionNavCollapseButton renders 1`] = `
|
||||
<EuiButtonIcon
|
||||
aria-label="Collapse side navigation"
|
||||
className="kbnSolutionNavCollapseButton"
|
||||
color="text"
|
||||
iconType="menuLeft"
|
||||
size="s"
|
||||
title="Collapse side navigation"
|
||||
/>
|
||||
`;
|
|
@ -10,7 +10,18 @@ exports[`SolutionNav accepts EuiSideNavProps 1`] = `
|
|||
paddingSize="none"
|
||||
title={
|
||||
<EuiTitle
|
||||
className="kbnSolutionNav__title"
|
||||
css={
|
||||
Object {
|
||||
"map": undefined,
|
||||
"name": "y6v9s0",
|
||||
"next": undefined,
|
||||
"styles": "
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
",
|
||||
"toString": [Function],
|
||||
}
|
||||
}
|
||||
id="SolutionNav_generated-id_heading"
|
||||
size="xs"
|
||||
>
|
||||
|
@ -102,7 +113,18 @@ exports[`SolutionNav accepts EuiSideNavProps 1`] = `
|
|||
className="kbnSolutionNav kbnSolutionNav--hidden"
|
||||
>
|
||||
<EuiTitle
|
||||
className="kbnSolutionNav__title"
|
||||
css={
|
||||
Object {
|
||||
"map": undefined,
|
||||
"name": "y6v9s0",
|
||||
"next": undefined,
|
||||
"styles": "
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
",
|
||||
"toString": [Function],
|
||||
}
|
||||
}
|
||||
id="SolutionNav_generated-id_heading"
|
||||
size="xs"
|
||||
>
|
||||
|
@ -201,7 +223,18 @@ exports[`SolutionNav accepts canBeCollapsed prop 1`] = `
|
|||
paddingSize="none"
|
||||
title={
|
||||
<EuiTitle
|
||||
className="kbnSolutionNav__title"
|
||||
css={
|
||||
Object {
|
||||
"map": undefined,
|
||||
"name": "y6v9s0",
|
||||
"next": undefined,
|
||||
"styles": "
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
",
|
||||
"toString": [Function],
|
||||
}
|
||||
}
|
||||
id="SolutionNav_generated-id_heading"
|
||||
size="xs"
|
||||
>
|
||||
|
@ -292,7 +325,18 @@ exports[`SolutionNav accepts canBeCollapsed prop 1`] = `
|
|||
className="kbnSolutionNav kbnSolutionNav--hidden"
|
||||
>
|
||||
<EuiTitle
|
||||
className="kbnSolutionNav__title"
|
||||
css={
|
||||
Object {
|
||||
"map": undefined,
|
||||
"name": "y6v9s0",
|
||||
"next": undefined,
|
||||
"styles": "
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
",
|
||||
"toString": [Function],
|
||||
}
|
||||
}
|
||||
id="SolutionNav_generated-id_heading"
|
||||
size="xs"
|
||||
>
|
||||
|
@ -390,7 +434,18 @@ exports[`SolutionNav accepts canBeCollapsed prop 2`] = `
|
|||
paddingSize="none"
|
||||
title={
|
||||
<EuiTitle
|
||||
className="kbnSolutionNav__title"
|
||||
css={
|
||||
Object {
|
||||
"map": undefined,
|
||||
"name": "y6v9s0",
|
||||
"next": undefined,
|
||||
"styles": "
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
",
|
||||
"toString": [Function],
|
||||
}
|
||||
}
|
||||
id="SolutionNav_generated-id_heading"
|
||||
size="xs"
|
||||
>
|
||||
|
@ -481,7 +536,18 @@ exports[`SolutionNav accepts canBeCollapsed prop 2`] = `
|
|||
className="kbnSolutionNav"
|
||||
>
|
||||
<EuiTitle
|
||||
className="kbnSolutionNav__title"
|
||||
css={
|
||||
Object {
|
||||
"map": undefined,
|
||||
"name": "y6v9s0",
|
||||
"next": undefined,
|
||||
"styles": "
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
",
|
||||
"toString": [Function],
|
||||
}
|
||||
}
|
||||
id="SolutionNav_generated-id_heading"
|
||||
size="xs"
|
||||
>
|
||||
|
@ -576,7 +642,18 @@ exports[`SolutionNav heading accepts more headingProps 1`] = `
|
|||
paddingSize="none"
|
||||
title={
|
||||
<EuiTitle
|
||||
className="kbnSolutionNav__title"
|
||||
css={
|
||||
Object {
|
||||
"map": undefined,
|
||||
"name": "y6v9s0",
|
||||
"next": undefined,
|
||||
"styles": "
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
",
|
||||
"toString": [Function],
|
||||
}
|
||||
}
|
||||
id="testID"
|
||||
size="xs"
|
||||
>
|
||||
|
@ -607,7 +684,18 @@ exports[`SolutionNav heading accepts more headingProps 1`] = `
|
|||
className="kbnSolutionNav kbnSolutionNav--hidden"
|
||||
>
|
||||
<EuiTitle
|
||||
className="kbnSolutionNav__title"
|
||||
css={
|
||||
Object {
|
||||
"map": undefined,
|
||||
"name": "y6v9s0",
|
||||
"next": undefined,
|
||||
"styles": "
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
",
|
||||
"toString": [Function],
|
||||
}
|
||||
}
|
||||
id="testID"
|
||||
size="xs"
|
||||
>
|
||||
|
@ -646,7 +734,18 @@ exports[`SolutionNav renders 1`] = `
|
|||
paddingSize="none"
|
||||
title={
|
||||
<EuiTitle
|
||||
className="kbnSolutionNav__title"
|
||||
css={
|
||||
Object {
|
||||
"map": undefined,
|
||||
"name": "y6v9s0",
|
||||
"next": undefined,
|
||||
"styles": "
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
",
|
||||
"toString": [Function],
|
||||
}
|
||||
}
|
||||
id="SolutionNav_generated-id_heading"
|
||||
size="xs"
|
||||
>
|
||||
|
@ -737,7 +836,18 @@ exports[`SolutionNav renders 1`] = `
|
|||
className="kbnSolutionNav kbnSolutionNav--hidden"
|
||||
>
|
||||
<EuiTitle
|
||||
className="kbnSolutionNav__title"
|
||||
css={
|
||||
Object {
|
||||
"map": undefined,
|
||||
"name": "y6v9s0",
|
||||
"next": undefined,
|
||||
"styles": "
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
",
|
||||
"toString": [Function],
|
||||
}
|
||||
}
|
||||
id="SolutionNav_generated-id_heading"
|
||||
size="xs"
|
||||
>
|
||||
|
@ -835,13 +945,35 @@ exports[`SolutionNav renders with icon 1`] = `
|
|||
paddingSize="none"
|
||||
title={
|
||||
<EuiTitle
|
||||
className="kbnSolutionNav__title"
|
||||
css={
|
||||
Object {
|
||||
"map": undefined,
|
||||
"name": "y6v9s0",
|
||||
"next": undefined,
|
||||
"styles": "
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
",
|
||||
"toString": [Function],
|
||||
}
|
||||
}
|
||||
id="SolutionNav_generated-id_heading"
|
||||
size="xs"
|
||||
>
|
||||
<h2>
|
||||
<KibanaSolutionAvatar
|
||||
className="kbnSolutionNav__titleAvatar"
|
||||
css={
|
||||
Object {
|
||||
"map": undefined,
|
||||
"name": "cmwv0a",
|
||||
"next": undefined,
|
||||
"styles": "
|
||||
margin-right: 12px;
|
||||
align-self: flex-start;
|
||||
",
|
||||
"toString": [Function],
|
||||
}
|
||||
}
|
||||
iconType="logoElastic"
|
||||
name="Solution"
|
||||
/>
|
||||
|
@ -931,13 +1063,35 @@ exports[`SolutionNav renders with icon 1`] = `
|
|||
className="kbnSolutionNav kbnSolutionNav--hidden"
|
||||
>
|
||||
<EuiTitle
|
||||
className="kbnSolutionNav__title"
|
||||
css={
|
||||
Object {
|
||||
"map": undefined,
|
||||
"name": "y6v9s0",
|
||||
"next": undefined,
|
||||
"styles": "
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
",
|
||||
"toString": [Function],
|
||||
}
|
||||
}
|
||||
id="SolutionNav_generated-id_heading"
|
||||
size="xs"
|
||||
>
|
||||
<h2>
|
||||
<KibanaSolutionAvatar
|
||||
className="kbnSolutionNav__titleAvatar"
|
||||
css={
|
||||
Object {
|
||||
"map": undefined,
|
||||
"name": "cmwv0a",
|
||||
"next": undefined,
|
||||
"styles": "
|
||||
margin-right: 12px;
|
||||
align-self: flex-start;
|
||||
",
|
||||
"toString": [Function],
|
||||
}
|
||||
}
|
||||
iconType="logoElastic"
|
||||
name="Solution"
|
||||
/>
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
// This size is also tracked with a variable in solution_nav.tsx, if updated
|
||||
// update there as well
|
||||
$solutionNavWidth: 248px;
|
||||
$solutionNavCollapsedWidth: $euiSizeXXL;
|
|
@ -1,49 +0,0 @@
|
|||
@import 'variables';
|
||||
|
||||
.kbnSolutionNavCollapseButton {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
left: $solutionNavWidth - $euiSize;
|
||||
top: $euiSizeL;
|
||||
z-index: 2;
|
||||
|
||||
@include euiCanAnimate {
|
||||
transition: opacity $euiAnimSpeedFast, left $euiAnimSpeedFast, background $euiAnimSpeedFast;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
transition-delay: 0s !important;
|
||||
}
|
||||
|
||||
.kbnSolutionNav__sidebar:hover &,
|
||||
&:hover,
|
||||
&:focus {
|
||||
opacity: 1;
|
||||
left: $solutionNavWidth - $euiSizeL;
|
||||
}
|
||||
|
||||
.kbnSolutionNav__sidebar:hover & {
|
||||
transition-delay: $euiAnimSpeedSlow * 2;
|
||||
}
|
||||
|
||||
&:not(&-isCollapsed) {
|
||||
background-color: $euiColorEmptyShade !important; // Override all states
|
||||
}
|
||||
}
|
||||
|
||||
// Make the button take up the entire area of the collapsed navigation
|
||||
.kbnSolutionNavCollapseButton-isCollapsed {
|
||||
opacity: 1 !important;
|
||||
transition-delay: 0s !important;
|
||||
left: 0 !important;
|
||||
right: auto;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
height: 100%;
|
||||
width: $solutionNavCollapsedWidth;
|
||||
border-radius: 0;
|
||||
// Keep the icon at the top instead of it getting shifted to the center of the page
|
||||
padding-top: $euiSizeL + $euiSizeS;
|
||||
align-items: flex-start;
|
||||
}
|
|
@ -7,24 +7,18 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { shallow } from 'enzyme';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { SolutionNavCollapseButton } from './collapse_button';
|
||||
|
||||
describe('SolutionNavCollapseButton', () => {
|
||||
test('renders', () => {
|
||||
const component = shallow(<SolutionNavCollapseButton isCollapsed={false} />);
|
||||
expect(component).toMatchSnapshot();
|
||||
expect(component.find('.kbnSolutionNavCollapseButton').prop('title')).toBe(
|
||||
'Collapse side navigation'
|
||||
);
|
||||
render(<SolutionNavCollapseButton isCollapsed={false} />);
|
||||
screen.getByTitle('Collapse side navigation');
|
||||
});
|
||||
|
||||
test('isCollapsed', () => {
|
||||
const component = shallow(<SolutionNavCollapseButton isCollapsed={true} />);
|
||||
expect(component).toMatchSnapshot();
|
||||
expect(component.find('.kbnSolutionNavCollapseButton').prop('title')).toBe(
|
||||
'Open side navigation'
|
||||
);
|
||||
render(<SolutionNavCollapseButton isCollapsed={true} />);
|
||||
screen.getByTitle('Open side navigation');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,12 +7,15 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import './collapse_button.scss';
|
||||
|
||||
import { css } from '@emotion/react';
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { EuiButtonIcon, EuiButtonIconPropsForButton } from '@elastic/eui';
|
||||
import {
|
||||
EuiButtonIcon,
|
||||
EuiButtonIconPropsForButton,
|
||||
euiCanAnimate,
|
||||
useEuiTheme,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export type SolutionNavCollapseButtonProps = Partial<EuiButtonIconPropsForButton> & {
|
||||
|
@ -38,17 +41,62 @@ export const SolutionNavCollapseButton = ({
|
|||
isCollapsed,
|
||||
...rest
|
||||
}: SolutionNavCollapseButtonProps) => {
|
||||
const classes = classNames(
|
||||
'kbnSolutionNavCollapseButton',
|
||||
{
|
||||
'kbnSolutionNavCollapseButton-isCollapsed': isCollapsed,
|
||||
},
|
||||
className
|
||||
);
|
||||
const { euiTheme } = useEuiTheme();
|
||||
const solutionNavWidth = '248px';
|
||||
|
||||
const styles = {
|
||||
base: css`
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
left: calc(${solutionNavWidth} - ${euiTheme.size.base});
|
||||
top: ${euiTheme.size.l};
|
||||
z-index: 2;
|
||||
|
||||
${euiCanAnimate} {
|
||||
transition: opacity ${euiTheme.animation.fast}, left ${euiTheme.animation.fast},
|
||||
background ${euiTheme.animation.fast};
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
transition-delay: 0s !important;
|
||||
}
|
||||
|
||||
.kbnSolutionNav__sidebar:hover &,
|
||||
&:hover,
|
||||
&:focus {
|
||||
opacity: 1;
|
||||
left: calc(${solutionNavWidth} - ${euiTheme.size.l});
|
||||
}
|
||||
|
||||
.kbnSolutionNav__sidebar:hover & {
|
||||
transition-delay: ${euiTheme.animation.slow} * 2;
|
||||
}
|
||||
`,
|
||||
isCollapsed: css`
|
||||
// Make the button take up the entire area of the collapsed navigation
|
||||
opacity: 1 !important;
|
||||
transition-delay: 0s !important;
|
||||
left: 0 !important;
|
||||
right: auto;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
height: 100%;
|
||||
width: ${euiTheme.size.xxl};
|
||||
border-radius: 0;
|
||||
// Keep the icon at the top instead of it getting shifted to the center of the page
|
||||
padding-top: calc(${euiTheme.size.l} + ${euiTheme.size.s});
|
||||
align-items: flex-start;
|
||||
`,
|
||||
notCollapsed: css`
|
||||
background-color: ${euiTheme.colors.backgroundBasePlain} !important; // Override all states
|
||||
`,
|
||||
};
|
||||
|
||||
return (
|
||||
<EuiButtonIcon
|
||||
className={classes}
|
||||
className={className}
|
||||
css={[styles.base, isCollapsed && styles.isCollapsed, !isCollapsed && styles.notCollapsed]}
|
||||
size="s"
|
||||
color="text"
|
||||
iconType={isCollapsed ? 'menuRight' : 'menuLeft'}
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
@import 'variables';
|
||||
|
||||
// Put the page background color in the flyout version too
|
||||
.kbnSolutionNav__flyout {
|
||||
background-color: $euiPageBackgroundColor;
|
||||
|
||||
.kbnSolutionNav {
|
||||
flex: auto; // Override default EuiPageSideBar flex CSS when in a flyout
|
||||
}
|
||||
}
|
||||
|
||||
.kbnSolutionNav {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@include euiYScroll;
|
||||
|
||||
@include euiBreakpoint('m', 'l', 'xl') {
|
||||
width: $solutionNavWidth;
|
||||
padding: $euiSizeL;
|
||||
}
|
||||
|
||||
&__title {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__titleAvatar {
|
||||
margin-right: $euiSizeM;
|
||||
align-self: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
.kbnSolutionNav--hidden {
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
|
||||
@include euiCanAnimate {
|
||||
transition: opacity $euiAnimSpeedFast $euiAnimSlightResistance;
|
||||
}
|
||||
}
|
|
@ -7,10 +7,10 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import './solution_nav.scss';
|
||||
|
||||
import React, { FC, useState, useMemo, useEffect } from 'react';
|
||||
import { css } from '@emotion/react';
|
||||
import classNames from 'classnames';
|
||||
import React, { FC, useState, useMemo, useEffect } from 'react';
|
||||
|
||||
import {
|
||||
EuiAvatarProps,
|
||||
EuiCollapsibleNavGroup,
|
||||
|
@ -28,6 +28,9 @@ import {
|
|||
useEuiTheme,
|
||||
useEuiThemeCSSVariables,
|
||||
EuiPageSidebar,
|
||||
useEuiOverflowScroll,
|
||||
useEuiBreakpoint,
|
||||
euiCanAnimate,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
@ -105,6 +108,7 @@ export const SolutionNav: FC<SolutionNavProps> = ({
|
|||
canBeCollapsed = true,
|
||||
...rest
|
||||
}) => {
|
||||
const { euiTheme } = useEuiTheme();
|
||||
const isSmallerBreakpoint = useIsWithinBreakpoints(mobileBreakpoints);
|
||||
const isMediumBreakpoint = useIsWithinBreakpoints(['m']);
|
||||
const isLargerBreakpoint = useIsWithinMinBreakpoint('l');
|
||||
|
@ -131,12 +135,18 @@ export const SolutionNav: FC<SolutionNavProps> = ({
|
|||
size="xs"
|
||||
id={headingID}
|
||||
data-test-subj={headingProps?.['data-test-subj']}
|
||||
className="kbnSolutionNav__title"
|
||||
css={css`
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
`}
|
||||
>
|
||||
<HeadingElement>
|
||||
{icon && (
|
||||
<KibanaSolutionAvatar
|
||||
className="kbnSolutionNav__titleAvatar"
|
||||
css={css`
|
||||
margin-right: ${euiTheme.size.m};
|
||||
align-self: flex-start;
|
||||
`}
|
||||
iconType={icon}
|
||||
name={name}
|
||||
/>
|
||||
|
@ -180,7 +190,6 @@ export const SolutionNav: FC<SolutionNavProps> = ({
|
|||
);
|
||||
}, [children, headingID, isCustomSideNav, isHidden, items, rest]);
|
||||
|
||||
const { euiTheme } = useEuiTheme();
|
||||
const navWidth = useMemo(() => {
|
||||
if (isLargerBreakpoint) {
|
||||
return isOpenOnDesktop ? FLYOUT_SIZE_CSS : euiTheme.size.xxl;
|
||||
|
@ -206,12 +215,34 @@ export const SolutionNav: FC<SolutionNavProps> = ({
|
|||
});
|
||||
}, [navWidth, setGlobalCSSVariables]);
|
||||
|
||||
const styles = {
|
||||
solutionNav: css`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
${useEuiOverflowScroll('y')};
|
||||
|
||||
${useEuiBreakpoint(['m', 'l', 'xl'])} {
|
||||
width: ${FLYOUT_SIZE_CSS};
|
||||
padding: ${euiTheme.size.l};
|
||||
}
|
||||
`,
|
||||
solutionNavHidden: css`
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
|
||||
${euiCanAnimate} {
|
||||
transition: opacity ${euiTheme.animation.fast} ${euiTheme.animation.resistance};
|
||||
}
|
||||
`,
|
||||
};
|
||||
return (
|
||||
<>
|
||||
{isSmallerBreakpoint && (
|
||||
// @ts-expect-error Mismatch in collapsible vs unconllapsible props
|
||||
<EuiCollapsibleNavGroup
|
||||
className={sideNavClasses}
|
||||
css={[styles.solutionNav, isHidden && styles.solutionNavHidden]}
|
||||
paddingSize="none"
|
||||
background="none"
|
||||
title={titleText}
|
||||
|
@ -234,10 +265,21 @@ export const SolutionNav: FC<SolutionNavProps> = ({
|
|||
side="left"
|
||||
size={FLYOUT_SIZE}
|
||||
closeButtonPosition={closeFlyoutButtonPosition}
|
||||
className="kbnSolutionNav__flyout"
|
||||
css={css`
|
||||
// Put the page background color in the flyout version too
|
||||
background-color: ${euiTheme.colors.backgroundBasePlain};
|
||||
|
||||
.kbnSolutionNav {
|
||||
flex: auto; // Override default EuiPageSideBar flex CSS when in a flyout
|
||||
}
|
||||
`}
|
||||
hideCloseButton={!canBeCollapsed}
|
||||
>
|
||||
<EuiPageSidebar className={sideNavClasses} hasEmbellish={true}>
|
||||
<EuiPageSidebar
|
||||
className={sideNavClasses}
|
||||
css={[styles.solutionNav, isHidden && styles.solutionNavHidden]}
|
||||
hasEmbellish={true}
|
||||
>
|
||||
{titleText}
|
||||
<EuiSpacer size="l" />
|
||||
{sideNavContent}
|
||||
|
@ -251,7 +293,10 @@ export const SolutionNav: FC<SolutionNavProps> = ({
|
|||
)}
|
||||
{isLargerBreakpoint && (
|
||||
<>
|
||||
<div className={sideNavClasses}>
|
||||
<div
|
||||
css={[styles.solutionNav, isHidden && styles.solutionNavHidden]}
|
||||
className={sideNavClasses}
|
||||
>
|
||||
{titleText}
|
||||
<EuiSpacer size="l" />
|
||||
{sideNavContent}
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
"jest",
|
||||
"node",
|
||||
"react",
|
||||
"@kbn/ambient-ui-types"
|
||||
"@kbn/ambient-ui-types",
|
||||
"@emotion/react/types/css-prop"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
|
|
|
@ -349,7 +349,6 @@ export function InternalDashboardTopNav({
|
|||
? setCustomHeaderActionMenu ?? undefined
|
||||
: setHeaderActionMenu
|
||||
}
|
||||
className={fullScreenMode ? 'kbnTopNavMenu-isFullScreen' : undefined}
|
||||
config={
|
||||
visibilityProps.showTopNavMenu
|
||||
? viewMode === 'edit'
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
@import 'top_nav_menu/index';
|
|
@ -7,8 +7,6 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import './index.scss';
|
||||
|
||||
import { PluginInitializerContext } from '@kbn/core/public';
|
||||
export function plugin(initializerContext: PluginInitializerContext) {
|
||||
return new NavigationPublicPlugin(initializerContext);
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
exports[`TopNavMenu when setMenuMountPoint is provided should render badges and search bar 1`] = `
|
||||
<div
|
||||
class="euiBadgeGroup kbnTopNavMenu__badgeGroup emotion-euiBadgeGroup-xs"
|
||||
class="euiBadgeGroup css-1dtftja"
|
||||
data-test-subj="kbn-top-nav-menu-badge-group"
|
||||
>
|
||||
<span
|
||||
class="euiBadge emotion-euiBadge-default"
|
||||
|
|
|
@ -28,6 +28,6 @@ exports[`TopNavMenu Should render emphasized item which should be clickable 1`]
|
|||
onClick={[Function]}
|
||||
size="s"
|
||||
>
|
||||
Test
|
||||
<ButtonContainer />
|
||||
</EuiButton>
|
||||
`;
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
.kbnTopNavMenu {
|
||||
button:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.kbnTopNavMenu__wrapper {
|
||||
&--hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.kbnTopNavMenu__badgeWrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.kbnTopNavMenu__badgeGroup {
|
||||
margin-right: $euiSizeM;
|
||||
}
|
||||
|
||||
.kbnTopNavMenu__betaBadgeItem {
|
||||
margin-right: $euiSizeS;
|
||||
vertical-align: middle;
|
||||
|
||||
button:hover &,
|
||||
button:focus & {
|
||||
text-decoration: underline;
|
||||
}
|
||||
button:hover & {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
|
@ -13,14 +13,14 @@ import { act } from 'react-dom/test-utils';
|
|||
import { MountPoint } from '@kbn/core/public';
|
||||
import { TopNavMenu } from './top_nav_menu';
|
||||
import { TopNavMenuData } from './top_nav_menu_data';
|
||||
import { findTestSubject, mountWithIntl } from '@kbn/test-jest-helpers';
|
||||
import { mountWithIntl } from '@kbn/test-jest-helpers';
|
||||
import { EuiToolTipProps } from '@elastic/eui';
|
||||
import type { TopNavMenuBadgeProps } from './top_nav_menu_badges';
|
||||
import { unifiedSearchMock } from '../mocks';
|
||||
|
||||
describe('TopNavMenu', () => {
|
||||
const WRAPPER_SELECTOR = '.kbnTopNavMenu__wrapper';
|
||||
const BADGES_GROUP_SELECTOR = '.kbnTopNavMenu__badgeGroup';
|
||||
const WRAPPER_SELECTOR = '[data-test-subj="kbn-top-nav-menu-wrapper"]';
|
||||
const BADGES_GROUP_SELECTOR = '[data-test-subj="kbn-top-nav-menu-badge-group"]';
|
||||
const TOP_NAV_ITEM_SELECTOR = 'TopNavMenuItem';
|
||||
const SEARCH_BAR_SELECTOR = 'AggregateQuerySearchBar';
|
||||
const menuItems: TopNavMenuData[] = [
|
||||
|
@ -121,7 +121,7 @@ describe('TopNavMenu', () => {
|
|||
className={'myCoolClass'}
|
||||
/>
|
||||
);
|
||||
expect(findTestSubject(component, 'top-nav').hasClass('kbnTopNavMenu')).toBe(true);
|
||||
expect(component.find(WRAPPER_SELECTOR).length).toBe(1);
|
||||
expect(component.find('.myCoolClass').length).toBeTruthy();
|
||||
});
|
||||
|
||||
|
@ -174,7 +174,7 @@ describe('TopNavMenu', () => {
|
|||
mountPoint(portalTarget);
|
||||
});
|
||||
|
||||
await refresh();
|
||||
refresh();
|
||||
|
||||
expect(component.find(SEARCH_BAR_SELECTOR).length).toBe(1);
|
||||
|
||||
|
@ -197,7 +197,7 @@ describe('TopNavMenu', () => {
|
|||
mountPoint(portalTarget);
|
||||
});
|
||||
|
||||
await refresh();
|
||||
refresh();
|
||||
|
||||
expect(component.find(SEARCH_BAR_SELECTOR).length).toBe(1);
|
||||
expect(portalTarget.querySelector(BADGES_GROUP_SELECTOR)).toMatchSnapshot();
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { css } from '@emotion/react';
|
||||
import React, { ReactElement } from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import type { MountPoint } from '@kbn/core/public';
|
||||
import { MountPointPortal } from '@kbn/react-kibana-mount';
|
||||
|
@ -81,11 +81,17 @@ export function TopNavMenu<QT extends AggregateQuery | Query = Query>(
|
|||
return <TopNavMenuBadges badges={badges} />;
|
||||
}
|
||||
|
||||
function renderMenu(className: string): ReactElement | null {
|
||||
function renderMenu(): ReactElement | null {
|
||||
return (
|
||||
<TopNavMenuItems
|
||||
config={config}
|
||||
className={className}
|
||||
className={props.className}
|
||||
data-test-subj="kbn-top-nav-menu-wrapper"
|
||||
css={css`
|
||||
button:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
`}
|
||||
popoverBreakpoints={props.popoverBreakpoints}
|
||||
/>
|
||||
);
|
||||
|
@ -100,18 +106,26 @@ export function TopNavMenu<QT extends AggregateQuery | Query = Query>(
|
|||
|
||||
function renderLayout() {
|
||||
const { setMenuMountPoint, visible } = props;
|
||||
const menuClassName = classNames('kbnTopNavMenu', props.className);
|
||||
const wrapperClassName = classNames('kbnTopNavMenu__wrapper', {
|
||||
'kbnTopNavMenu__wrapper--hidden': visible === false,
|
||||
});
|
||||
const styles = {
|
||||
badgeWrapper: css`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`,
|
||||
hidden: css`
|
||||
display: none;
|
||||
`,
|
||||
};
|
||||
if (setMenuMountPoint) {
|
||||
const badgesEl = renderBadges();
|
||||
const menuEl = renderMenu(menuClassName);
|
||||
const menuEl = renderMenu();
|
||||
return (
|
||||
<>
|
||||
{(badgesEl || menuEl) && (
|
||||
<MountPointPortal setMountPoint={setMenuMountPoint}>
|
||||
<span className={`${wrapperClassName} kbnTopNavMenu__badgeWrapper`}>
|
||||
<span
|
||||
className="kbnTopNavMenu__wrapper"
|
||||
css={[styles.badgeWrapper, visible === false && styles.hidden]}
|
||||
>
|
||||
{badgesEl}
|
||||
{menuEl}
|
||||
</span>
|
||||
|
@ -124,7 +138,7 @@ export function TopNavMenu<QT extends AggregateQuery | Query = Query>(
|
|||
} else {
|
||||
return (
|
||||
<>
|
||||
<span className={wrapperClassName}>{renderMenu(menuClassName)}</span>
|
||||
<span css={[visible === false && styles.hidden]}>{renderMenu()}</span>
|
||||
{renderSearchBar()}
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -7,9 +7,18 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { EuiBadge, EuiBadgeGroup, EuiToolTip, EuiBadgeProps, EuiToolTipProps } from '@elastic/eui';
|
||||
import { css } from '@emotion/react';
|
||||
import React, { Fragment, ReactElement } from 'react';
|
||||
|
||||
import {
|
||||
EuiBadge,
|
||||
EuiBadgeGroup,
|
||||
EuiToolTip,
|
||||
EuiBadgeProps,
|
||||
EuiToolTipProps,
|
||||
useEuiTheme,
|
||||
} from '@elastic/eui';
|
||||
|
||||
export type TopNavMenuBadgeProps = EuiBadgeProps & {
|
||||
badgeText: string;
|
||||
toolTipProps?: Partial<EuiToolTipProps>;
|
||||
|
@ -17,9 +26,17 @@ export type TopNavMenuBadgeProps = EuiBadgeProps & {
|
|||
};
|
||||
|
||||
export const TopNavMenuBadges = ({ badges }: { badges: TopNavMenuBadgeProps[] | undefined }) => {
|
||||
const { euiTheme } = useEuiTheme();
|
||||
if (!badges || badges.length === 0) return null;
|
||||
return (
|
||||
<EuiBadgeGroup className="kbnTopNavMenu__badgeGroup">{badges.map(createBadge)}</EuiBadgeGroup>
|
||||
<EuiBadgeGroup
|
||||
css={css`
|
||||
margin-right: ${euiTheme.size.m};
|
||||
`}
|
||||
data-test-subj="kbn-top-nav-menu-badge-group"
|
||||
>
|
||||
{badges.map(createBadge)}
|
||||
</EuiBadgeGroup>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -8,7 +8,10 @@
|
|||
*/
|
||||
|
||||
import { upperFirst, isFunction, omit } from 'lodash';
|
||||
|
||||
import { css } from '@emotion/react';
|
||||
import React, { MouseEvent } from 'react';
|
||||
|
||||
import {
|
||||
EuiToolTip,
|
||||
EuiButton,
|
||||
|
@ -16,6 +19,7 @@ import {
|
|||
EuiBetaBadge,
|
||||
EuiButtonColor,
|
||||
EuiButtonIcon,
|
||||
useEuiTheme,
|
||||
} from '@elastic/eui';
|
||||
import { TopNavMenuData } from './top_nav_menu_data';
|
||||
|
||||
|
@ -35,11 +39,27 @@ export function TopNavMenuItem(props: TopNavMenuItemProps) {
|
|||
return val!;
|
||||
}
|
||||
|
||||
function getButtonContainer() {
|
||||
function ButtonContainer() {
|
||||
const { euiTheme } = useEuiTheme();
|
||||
if (props.badge) {
|
||||
return (
|
||||
<>
|
||||
<EuiBetaBadge className="kbnTopNavMenu__betaBadgeItem" {...props.badge} size="s" />
|
||||
<EuiBetaBadge
|
||||
css={css`
|
||||
margin-right: ${euiTheme.size.s};
|
||||
vertical-align: middle;
|
||||
|
||||
button:hover &,
|
||||
button:focus & {
|
||||
text-decoration: underline;
|
||||
}
|
||||
button:hover & {
|
||||
cursor: pointer;
|
||||
}
|
||||
`}
|
||||
{...props.badge}
|
||||
size="s"
|
||||
/>
|
||||
{upperFirst(props.label || props.id!)}
|
||||
</>
|
||||
);
|
||||
|
@ -94,11 +114,11 @@ export function TopNavMenuItem(props: TopNavMenuItemProps) {
|
|||
{...commonButtonProps}
|
||||
fill={props.fill ?? true}
|
||||
>
|
||||
{getButtonContainer()}
|
||||
<ButtonContainer />
|
||||
</EuiButton>
|
||||
) : (
|
||||
<EuiHeaderLink size="s" {...commonButtonProps} {...overrideProps}>
|
||||
{getButtonContainer()}
|
||||
<ButtonContainer />
|
||||
</EuiHeaderLink>
|
||||
);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue