Skip to content

Commit

Permalink
feat(cc-header-orga): add a footer slot and small design tweaks
Browse files Browse the repository at this point in the history
Fixes #869
  • Loading branch information
florian-sanders-cc committed Dec 11, 2023
1 parent 65535f9 commit dfb4624
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 73 deletions.
136 changes: 65 additions & 71 deletions src/components/cc-header-orga/cc-header-orga.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import '../cc-notice/cc-notice.js';
import { css, html, LitElement } from 'lit';
import { classMap } from 'lit/directives/class-map.js';
import {
iconRemixCheckboxCircleFill as iconBadge,
iconRemixVerifiedBadgeFill as iconBadge,
iconRemixPhoneFill as iconPhone,
} from '../../assets/cc-remix.icons.js';

import { i18n } from '../../lib/i18n.js';
import { skeletonStyles } from '../../styles/skeleton.js';
import { linkStyles } from '../../templates/cc-link/cc-link.js';

/**
* @typedef {import('./cc-header-orga.types.js').HeaderOrgaState} HeaderOrgaState
Expand All @@ -18,6 +19,8 @@ import { skeletonStyles } from '../../styles/skeleton.js';
/**
* A component to display various info about an orga (name and enterprise status).
*
* @slot footer - An area displayed at the bottom of the header. Content should be short. The element you slot gets a default styling (background color, top border and padding).
*
* @cssdisplay block
*/
export class CcHeaderOrga extends LitElement {
Expand Down Expand Up @@ -56,42 +59,50 @@ export class CcHeaderOrga extends LitElement {

if (this.orga.state === 'loading') {
return this._renderHeader({
skeleton: true,
name: '??????????????????????????',
skeleton: true,
});
}

if (this.orga.state === 'loaded') {
return this._renderHeader({
skeleton: false,
name: this.orga.name,
avatar: this.orga.avatar,
cleverEnterprise: this.orga.cleverEnterprise,
emergencyNumber: this.orga.emergencyNumber,
skeleton: false,
});
}
}

_renderHeader ({ skeleton, name, avatar = null, cleverEnterprise = false, emergencyNumber = null }) {
_renderHeader ({ name, avatar = null, cleverEnterprise = false, emergencyNumber = null, skeleton = false }) {

return html`
<div class="wrapper ${classMap({ enterprise: cleverEnterprise })}">
${this._renderAvatar(skeleton, avatar, name)}
<div class="details">
<div class="name ${classMap({ skeleton })}">${name}</div>
${cleverEnterprise ? html`
<div class="spacer"></div>
<cc-badge weight="strong" intent="info" .icon=${iconBadge}>Clever Cloud Enterprise</cc-badge>
` : ''}
</div>
${(emergencyNumber != null) ? html`
<div class="hotline">
<div class="hotline_label">${i18n('cc-header-orga.hotline')}</div>
<a class="hotline_number" href="tel:${emergencyNumber}">
<cc-badge weight="outlined" intent="info" .icon=${iconPhone}>${emergencyNumber}</cc-badge>
</a>
<div class="wrapper">
<div class="header-body">
<p class="identity">
${this._renderAvatar(skeleton, avatar, name)}
<span class="name ${classMap({ skeleton })}">${name}</span>
</p>
<div class="enterprise">
<p class="enterprise-row">
${cleverEnterprise ? html`
<cc-icon .icon=${iconBadge}></cc-icon>
<span lang="en">Clever Cloud Enterprise</span>
` : ''}
</p>
${(emergencyNumber != null) ? html`
<p class="enterprise-row">
<cc-icon .icon=${iconPhone}>${emergencyNumber}</cc-icon>
<span>
${i18n('cc-header-orga.hotline')}
<a class="cc-link" href="tel:${emergencyNumber}">${emergencyNumber}</a>
</span>
</p>
` : ''}
</div>
</div>
` : ''}
<slot name="footer"></slot>
</div>
`;
}
Expand All @@ -118,37 +129,49 @@ export class CcHeaderOrga extends LitElement {

static get styles () {
return [
linkStyles,
skeletonStyles,
// language=CSS
css`
:host {
display: block;
}
p {
margin: 0;
}
cc-notice {
width: 100%;
}
.wrapper {
display: flex;
overflow: hidden;
flex-wrap: wrap;
padding: 1em;
border: 1px solid var(--cc-color-border-neutral, #aaa);
background-color: var(--cc-color-bg-default, #fff);
border-radius: var(--cc-border-radius-default, 0.25em);
}
.header-body {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
padding: 1em;
gap: 1em;
}
.wrapper.enterprise {
border-width: 2px;
border-color: var(--cc-color-bg-primary);
.identity {
display: flex;
align-items: center;
gap: 1em;
}
.logo,
.initials {
width: 3.25em;
height: 3.25em;
flex: 0 0 auto;
border-radius: var(--cc-border-radius-default, 0.25em);
}
Expand All @@ -163,61 +186,32 @@ export class CcHeaderOrga extends LitElement {
font-size: 0.85em;
}
.details,
.hotline {
display: flex;
flex-direction: column;
align-items: flex-start;
}
.details {
flex: 100 1 max-content;
justify-content: center;
row-gap: 0.2em;
}
.hotline {
flex: 1 1 auto;
justify-content: space-between;
}
.name {
font-size: 1.1em;
font-size: 1.3em;
font-weight: bold;
}
.hotline_number {
border-radius: 1em;
}
.hotline_number:focus,
.hotline_number:active {
outline: 0;
}
/* We can do this because we set a visible focus state */
.hotline_number::-moz-focus-inner {
border: 0;
.enterprise {
display: flex;
flex-direction: column;
gap: 0.5em;
}
.hotline_number:focus {
outline: var(--cc-focus-outline, #000 solid 2px);
outline-offset: var(--cc-focus-outline-offset, 2px);
.enterprise-row {
display: flex;
gap: 0.5em;
}
.hotline_number:hover {
box-shadow: 0 1px 3px rgb(0 0 0 / 40%);
}
.enterprise-row cc-icon {
--cc-icon-color: var(--cc-color-text-primary-highlight);
.hotline_number:active {
box-shadow: none;
flex: 0 0 auto;
}
.hotline_number cc-badge {
/* Prevent space below badge because of text lines */
display: flex;
text-decoration: underline;
::slotted([slot='footer']) {
padding: 0.5em 1em;
border-top: solid 1px var(--cc-color-border-neutral-weak);
background-color: var(--cc-color-bg-neutral);
}
/* SKELETON */
Expand Down
74 changes: 72 additions & 2 deletions src/components/cc-header-orga/cc-header-orga.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ export default {

const conf = {
component: 'cc-header-orga',
// language=CSS
css: `
[slot='footer'] p {
margin: 0;
}
`,
};

const DEFAULT_ORGA = {
Expand All @@ -19,6 +25,10 @@ const DEFAULT_ORGA = {
emergencyNumber: '+33 6 00 00 00 00',
};

const DEFAULT_SLOTTED_CONTENT = `<div slot="footer">
<p>This content is slotted. You should keep it short because it's a header component.</p>
</div>`;

export const defaultStory = makeStory(conf, {
items: [{
orga: DEFAULT_ORGA,
Expand All @@ -45,16 +55,30 @@ export const dataLoadedWithClassicClient = makeStory(conf, {
items: [{
orga: {
...DEFAULT_ORGA,
name: 'ACME Startup',
cleverEnterprise: false,
emergencyNumber: null,
},
}],
});

export const dataLoadedWithClassicClientAndSlottedContent = makeStory(conf, {
items: [{
orga: {
...DEFAULT_ORGA,
name: 'ACME Startup',
cleverEnterprise: false,
emergencyNumber: null,
},
innerHTML: DEFAULT_SLOTTED_CONTENT,
}],
});

export const dataLoadedWithClassicClientNoAvatar = makeStory(conf, {
items: [{
orga: {
...DEFAULT_ORGA,
name: 'ACME Startup',
avatar: null,
cleverEnterprise: false,
emergencyNumber: null,
Expand All @@ -71,17 +95,60 @@ export const dataLoadedWithEnterpriseClient = makeStory(conf, {
}],
});

export const dataLoadedWithEnterpriseClientAndSlottedContent = makeStory(conf, {
items: [{
orga: {
...DEFAULT_ORGA,
emergencyNumber: null,
},
innerHTML: DEFAULT_SLOTTED_CONTENT,
}],
});

export const dataLoadedWithEnterpriseClientEmergencyNumber = makeStory(conf, {
items: [{
orga: DEFAULT_ORGA,
}],
});

export const dataLoadedWithEnterpriseClientEmergencyNumberAndSlottedContent = makeStory(conf, {
items: [{
orga: DEFAULT_ORGA,
innerHTML: DEFAULT_SLOTTED_CONTENT,
}],
});

export const dataLoadedWithSlottedContentCustomStyling = makeStory(conf, {
css: `
[slot="footer"] {
background-color: var(--cc-color-bg-primary);
color: white;
padding: 2em;
margin: 0;
border-top: solid 2px #000;
}
`,
items: [{
orga: DEFAULT_ORGA,
innerHTML: `
<div slot="footer">
<p>The slotted content container has a default styling: <code>background-color</code>, <code>padding</code> and <code>border-top</code>.</p>
<p>You may change any of these by styling the tag on which you have added the <code>slot="footer"</code> attribute.</p>
<p><strong>Caution:</strong> the default styling was designed to remain consistent with the header body and avoid drawing too much attention to it. You should stick to the default styling as much as possible and stay as close as possible to it. This example is only here to demo that anything can be customized.
</div>
`,
}],
});

export const simulations = makeStory(conf, {
items: [{}, {}, {}],
items: [{}, {}, { innerHTML: DEFAULT_SLOTTED_CONTENT }, {}],
simulations: [
storyWait(3000, ([component, componentNoAvatar, componentError]) => {
storyWait(3000, ([component, componentNoAvatar, componentWithSlottedContent, componentError]) => {
component.orga = DEFAULT_ORGA;
componentWithSlottedContent.orga = {
...DEFAULT_ORGA,
emergencyNumber: null,
};
componentNoAvatar.orga = {
...DEFAULT_ORGA,
avatar: null,
Expand All @@ -96,8 +163,11 @@ enhanceStoriesNames({
skeleton,
error,
dataLoadedWithClassicClient,
dataLoadedWithClassicClientAndSlottedContent,
dataLoadedWithClassicClientNoAvatar,
dataLoadedWithEnterpriseClient,
dataLoadedWithEnterpriseClientEmergencyNumber,
dataLoadedWithEnterpriseClientEmergencyNumberAndSlottedContent,
dataLoadedWithSlottedContentCustomStyling,
simulations,
});

0 comments on commit dfb4624

Please sign in to comment.