[SharedUX] Add custom branding to ExitFullScreenButton (#150620)

## Summary

This PR replaces the Elastic logo with the custom logo in
`ExitFullScreenButton` component.

Without custom logo:
<img width="185" alt="Screenshot 2023-02-13 at 12 49 59"
src="https://user-images.githubusercontent.com/1937956/218451086-5cf3122b-a167-4b39-8311-1cead1f7afc5.png">


With custom logo:
<img width="163" alt="Screenshot 2023-02-13 at 12 53 16"
src="https://user-images.githubusercontent.com/1937956/218451270-db004b4a-b089-4963-9122-7d3f7b231372.png">



### Checklist

Delete any items that are not applicable to this PR.

~- [ ] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)~
~- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials~
- [X] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [X] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [X] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
~- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)~
- [x] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
~- [ ] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)~


### For maintainers

- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Maja Grubic 2023-02-14 10:27:35 +01:00 committed by GitHub
parent ea646dfe04
commit 868fc247a9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 99 additions and 5 deletions

View file

@ -41,6 +41,54 @@ exports[`<ExitFullScreenButton /> with kibana services is rendered 1`] = `
</div>
`;
exports[`<ExitFullScreenButton /> with kibana services renders custom logo 1`] = `
<div>
<p
aria-live="polite"
class="emotion-euiScreenReaderOnly"
>
In full screen mode, press ESC to exit.
</p>
<button
class="exitFullScreenButton"
css="You have tried to stringify object returned from \`css\` function. It isn't supposed to be used directly (e.g. as value of the \`className\` prop), but rather handed to emotion so it can handle it (e.g. as value of \`css\` prop)."
data-test-subj="exitFullScreenModeButton"
>
<span
class="euiFlexGroup emotion-euiFlexGroup-s-flexStart-center-row"
>
<span
class="euiFlexItem emotion-euiFlexItem-growZero"
>
<figure
class="euiImageWrapper emotion-euiImageWrapper"
>
<img
alt="customLogo"
class="euiImage emotion-euiImage-customSize"
src="imageSrcAsBase64encodedstring"
style="max-width: 16px; max-height: 16px;"
/>
</figure>
</span>
<span
class="euiFlexItem emotion-euiFlexItem-growZero"
data-test-subj="exitFullScreenModeText"
>
Exit full screen
</span>
<span
class="euiFlexItem emotion-euiFlexItem-growZero"
>
<span
data-euiicon-type="fullScreenExit"
/>
</span>
</span>
</button>
</div>
`;
exports[`<ExitFullScreenButton /> with manual services is rendered 1`] = `
<div>
<p

View file

@ -12,6 +12,7 @@ import {
EuiFlexGroup,
EuiFlexItem,
EuiIcon,
EuiImage,
useEuiTheme,
makeHighContrastColor,
} from '@elastic/eui';
@ -37,7 +38,7 @@ const description = i18n.translate(
/**
* A presentational component that renders a button designed to exit "full screen" mode.
*/
export const ExitFullScreenButton = ({ onClick, className }: Props) => {
export const ExitFullScreenButton = ({ onClick, className, customLogo }: Props) => {
const { euiTheme } = useEuiTheme();
const { colors, size, border } = euiTheme;
@ -63,7 +64,11 @@ export const ExitFullScreenButton = ({ onClick, className }: Props) => {
>
<EuiFlexGroup component="span" responsive={false} alignItems="center" gutterSize="s">
<EuiFlexItem component="span" grow={false}>
<EuiIcon type="logoElastic" size="m" />
{customLogo ? (
<EuiImage src={customLogo} size={16} alt="customLogo" />
) : (
<EuiIcon type="logoElastic" size="m" />
)}
</EuiFlexItem>
<EuiFlexItem component="span" grow={false} data-test-subj="exitFullScreenModeText">
{text}

View file

@ -17,6 +17,7 @@ import {
import { ExitFullScreenButton } from './exit_full_screen_button';
import { ExitFullScreenButtonKibanaProvider, ExitFullScreenButtonProvider } from './services';
import { of } from 'rxjs';
const componentServices = getExitFullScreenButtonServicesMock();
const kibanaServices = getExitFullScreenButtonKibanaDependenciesMock();
@ -103,6 +104,16 @@ describe('<ExitFullScreenButton />', () => {
expect(kibanaServices.coreStart.chrome.setIsVisible).toHaveBeenCalledTimes(0);
});
test('renders custom logo', () => {
kibanaServices.coreStart.customBranding.customBranding$ = of({
logo: 'imageSrcAsBase64encodedstring',
});
const component = kibanaMount(
<ExitFullScreenButton onExit={jest.fn()} toggleChrome={false} />
);
expect(component.render()).toMatchSnapshot();
});
describe('onExit', () => {
const onExitHandler = jest.fn();
let component: ReactWrapper;

View file

@ -13,6 +13,7 @@ import useMountedState from 'react-use/lib/useMountedState';
import type { ExitFullScreenButtonProps as Props } from '@kbn/shared-ux-button-exit-full-screen-types';
import useObservable from 'react-use/lib/useObservable';
import { ExitFullScreenButton as Component } from './exit_full_screen_button.component';
import { useServices } from './services';
@ -22,8 +23,10 @@ import { useServices } from './services';
*/
export const ExitFullScreenButton = ({ onExit = () => {}, toggleChrome = true }: Props) => {
const { euiTheme } = useEuiTheme();
const { setIsFullscreen } = useServices();
const { setIsFullscreen, customBranding$ } = useServices();
const isMounted = useMountedState();
const customBranding = useObservable(customBranding$);
const customLogo = customBranding?.logo;
const onClick = useCallback(() => {
if (toggleChrome) {
@ -70,5 +73,5 @@ export const ExitFullScreenButton = ({ onExit = () => {}, toggleChrome = true }:
z-index: 5;
`;
return <Component css={buttonCSS} {...{ onClick }} />;
return <Component css={buttonCSS} customLogo={customLogo} {...{ onClick }} />;
};

View file

@ -43,6 +43,7 @@ export const ExitFullScreenButtonKibanaProvider: FC<ExitFullScreenButtonKibanaDe
setIsFullscreen: (isFullscreen: boolean) => {
services.coreStart.chrome.setIsVisible(!isFullscreen);
},
customBranding$: services.coreStart.customBranding.customBranding$,
}}
>
{children}

View file

@ -10,6 +10,7 @@ import {
ExitFullScreenButtonKibanaDependencies,
ExitFullScreenButtonServices,
} from '@kbn/shared-ux-button-exit-full-screen-types';
import { of } from 'rxjs';
/**
* Return a Jest mock of the services for the `ExitFullScreenButton` component.
@ -17,6 +18,7 @@ import {
export const getServicesMock = (): ExitFullScreenButtonServices => {
return {
setIsFullscreen: jest.fn(),
customBranding$: of({}),
};
};
@ -29,6 +31,9 @@ export const getKibanaDependenciesMock = (): ExitFullScreenButtonKibanaDependenc
chrome: {
setIsVisible: jest.fn(),
},
customBranding: {
customBranding$: of({}),
},
},
};
};

View file

@ -12,6 +12,7 @@ import type {
ExitFullScreenButtonProps as Props,
ExitFullScreenButtonServices,
} from '@kbn/shared-ux-button-exit-full-screen-types';
import { of } from 'rxjs';
type PropArguments = Pick<Props, 'toggleChrome'>;
@ -51,6 +52,7 @@ export class StorybookMock extends AbstractStorybookMock<
setIsFullscreen: (isFullscreen: boolean) => {
action('setIsFullscreen')(isFullscreen);
},
customBranding$: of({}),
};
}
}

View file

@ -7,6 +7,8 @@
*/
import { MouseEventHandler, HTMLAttributes } from 'react';
import { Observable } from 'rxjs';
import { CustomBranding } from '@kbn/core-custom-branding-common';
/**
* Abstract external services for this component.
@ -14,6 +16,8 @@ import { MouseEventHandler, HTMLAttributes } from 'react';
export interface Services {
/** Function to invoke to set the application to full-screen mode */
setIsFullscreen: (isFullscreen: boolean) => void;
/** Observable that emits the value of custom branding, if set*/
customBranding$: Observable<CustomBranding>;
}
/**
@ -29,6 +33,9 @@ export interface KibanaDependencies {
chrome: {
setIsVisible: (isVisible: boolean) => void;
};
customBranding: {
customBranding$: Observable<CustomBranding>;
};
};
}
@ -55,4 +62,6 @@ export interface ExitFullScreenButtonComponentProps
extends Pick<HTMLAttributes<HTMLDivElement>, 'className'> {
/** Handler to invoke when one clicks the button. */
onClick: MouseEventHandler<HTMLButtonElement>;
/** If set, custom logo is displayed instead of Elastic logo */
customLogo?: string;
}

View file

@ -9,5 +9,8 @@
],
"exclude": [
"target/**/*",
],
"kbn_references": [
"@kbn/core-custom-branding-common",
]
}

View file

@ -120,6 +120,7 @@ export class DashboardContainer extends Container<InheritedChildInput, Dashboard
private dashboardSavedObjectService: DashboardSavedObjectService;
private theme$;
private chrome;
private customBranding;
constructor(
initialInput: DashboardContainerInput,
@ -152,6 +153,7 @@ export class DashboardContainer extends Container<InheritedChildInput, Dashboard
theme: { theme$: this.theme$ },
},
chrome: this.chrome,
customBranding: this.customBranding,
} = pluginServices.getServices());
this.initialSavedDashboardId = dashboardContainerInputIsByValue(this.input)
@ -422,7 +424,9 @@ export class DashboardContainer extends Container<InheritedChildInput, Dashboard
const { Wrapper: DashboardReduxWrapper } = this.reduxEmbeddableTools;
ReactDOM.render(
<I18nProvider>
<ExitFullScreenButtonKibanaProvider coreStart={{ chrome: this.chrome }}>
<ExitFullScreenButtonKibanaProvider
coreStart={{ chrome: this.chrome, customBranding: this.customBranding }}
>
<KibanaThemeProvider theme$={this.theme$}>
<DashboardReduxWrapper>
<DashboardViewport />

View file

@ -16,5 +16,6 @@ export const customBrandingServiceFactory: CustomBrandingServiceFactory = () =>
const pluginMock = coreMock.createStart();
return {
hasCustomBranding$: pluginMock.customBranding.hasCustomBranding$,
customBranding$: pluginMock.customBranding.customBranding$,
};
};

View file

@ -19,5 +19,6 @@ export const customBrandingServiceFactory: CustomBrandingServiceFactory = ({ cor
const { customBranding } = coreStart;
return {
hasCustomBranding$: customBranding.hasCustomBranding$,
customBranding$: customBranding.customBranding$,
};
};

View file

@ -10,4 +10,5 @@ import type { CustomBrandingStart } from '@kbn/core-custom-branding-browser';
export interface DashboardCustomBrandingService {
hasCustomBranding$: CustomBrandingStart['hasCustomBranding$'];
customBranding$: CustomBrandingStart['customBranding$'];
}