Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
5f5f712
HDS-4745 Implement height toggle functionality, add basic styling, up…
KristinLBradley Apr 16, 2025
acf985f
HDS-4745 Add changeset
KristinLBradley Apr 16, 2025
432d802
HDS-4745 Add tests for heigh toggle feature
KristinLBradley Apr 16, 2025
4cb4870
HDS-4745 Fix issue with determining height of content with container …
KristinLBradley Apr 16, 2025
3be6dc8
HDS-4745 Add gradient that appears behin Show more button, refactor r…
KristinLBradley Apr 18, 2025
8c1695e
HDS-4745 Fix linting error
KristinLBradley Apr 18, 2025
53797c0
HDS-4745 Tweak height of gradient to match visual design, remove bott…
KristinLBradley Apr 18, 2025
ce6896b
HDS-4745 Make border of numbering extend to bottom of code block
KristinLBradley Apr 18, 2025
65e6a79
HDS-4745 Fix linting, change height toggle button to secondary color
KristinLBradley Apr 18, 2025
b517c12
HDS-4745 Implement dark button styles following copy button
KristinLBradley Apr 18, 2025
52eed80
HDS-4745 Fix issue with numbering border on expanded CodeBlock
KristinLBradley Apr 18, 2025
5305191
HDS-4745 Fix interactivity when hovering overlay footer, undo & clean…
KristinLBradley Apr 18, 2025
72ae18e
HDS-4745 Tweak padding and positioning styles for overlay footer and …
KristinLBradley Apr 18, 2025
0b61f8a
Fix: Refactor toggle button to prevent focus being lost
dchyun Apr 25, 2025
163e5f6
Feat: Update toggle icon and label
dchyun Apr 25, 2025
d992568
Fix: Footer padding alignment to design
dchyun May 1, 2025
d33b796
Fix: Remove footer styles affecting line highlighting
dchyun May 2, 2025
f5b3444
Fix: Remove comments
dchyun May 6, 2025
634b674
Fix: Linting
dchyun May 6, 2025
feab0fe
Fix: Footer gradient positioning
dchyun May 6, 2025
b348338
Guidelines update
andgen404 May 6, 2025
3833ae4
Fix: Revert guidelines update
dchyun May 6, 2025
bb8309b
Fix: Update expanded state class name
dchyun May 7, 2025
668aace
Fix: Footer gradient horizontal overflow
dchyun May 7, 2025
f0f7ddc
Fix: Linting
dchyun May 7, 2025
a8a8df0
Fix: Horizontal overflow gradient placement and code block spacing
dchyun May 7, 2025
000dbea
Fix: Gradient horizontal scrolling
dchyun May 7, 2025
863346b
Fix: Gradient overflow, line numbers border
dchyun May 8, 2025
5dfc2ae
Fix: scss variable syntax
dchyun May 8, 2025
4ad22e6
Fix: Line wrapping with long string
dchyun May 9, 2025
d65b2a4
Chore: Style clean up, add comments
dchyun May 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fresh-kangaroos-sing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@hashicorp/design-system-components": minor
---

`CodeBlock` - Added height toggle control which is present when a `maxHeight` is set and code content height exceeds the `maxHeight` value
14 changes: 13 additions & 1 deletion packages/components/src/components/hds/code-block/index.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
{{! content within pre tag is whitespace-sensitive; do not add new lines! }}
<pre
class="hds-code-block__code"
{{style maxHeight=@maxHeight}}
{{style maxHeight=this.maxHeight}}
data-line={{@highlightLines}}
data-start={{@lineNumberStart}}
id={{this._preCodeId}}
Expand All @@ -30,4 +30,16 @@
/>
{{/if}}
</div>
{{#if this.showFooter}}
<div class="hds-code-block__overlay-footer">
<Hds::Button
class="hds-code-block__height-toggle-button"
@text={{if this._isExpanded "Show less code" "Show more code"}}
@color="secondary"
@icon={{if this._isExpanded "unfold-close" "unfold-open"}}
@size="small"
{{on "click" this.toggleExpanded}}
/>
</div>
{{/if}}
</div>
85 changes: 44 additions & 41 deletions packages/components/src/components/hds/code-block/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,19 +69,14 @@ export interface HdsCodeBlockSignature {

export default class HdsCodeBlock extends Component<HdsCodeBlockSignature> {
@tracked private _prismCode: SafeString = htmlSafe('');
@tracked private _isExpanded: boolean = false;
@tracked private _codeContentHeight: number = 0;
@tracked private _codeContainerHeight: number = 0;

/**
* Generates a unique ID for the code content
*
* @param _preCodeId
*/
// Generates a unique ID for the code content
private _preCodeId = 'pre-code-' + guidFor(this);

/**
* @param code
* @type {string}
* @description code text content for the CodeBlock
*/
// code text content for the CodeBlock
get code(): string {
const code = this.args.value;

Expand All @@ -98,42 +93,34 @@ export default class HdsCodeBlock extends Component<HdsCodeBlockSignature> {
return code;
}

/**
* @param language
* @type {string}
* @default undefined
* @description name of coding language used within CodeBlock for syntax highlighting
*/
get maxHeight(): string | undefined {
return this._isExpanded ? 'none' : this.args.maxHeight;
}

// Shows overlay footer if maxHeight is set and the pre tag content height is greater than the pre tag height
get showFooter(): boolean {
if (this.args.maxHeight) {
return this._codeContentHeight > this._codeContainerHeight;
}
return false;
}

// Name of coding language used within CodeBlock for syntax highlighting
get language(): HdsCodeBlockLanguages | undefined {
return this.args.language ?? undefined;
}

/**
* @param hasLineNumbers
* @type {boolean}
* @default true
* @description Displays line numbers if true
*/
// Displays line numbers if true
get hasLineNumbers(): boolean {
return this.args.hasLineNumbers ?? true;
}

/**
* @param isStandalone
* @type {boolean}
* @default true
* @description Make CodeBlock container corners appear rounded
*/
// Make CodeBlock container corners appear rounded (the standalone variation)
get isStandalone(): boolean {
return this.args.isStandalone ?? true;
}

/**
* @param hasLineWrapping
* @type {boolean}
* @default false
* @description Make text content wrap on multiple lines
*/
// Make text content wrap to multiple lines
get hasLineWrapping(): boolean {
return this.args.hasLineWrapping ?? false;
}
Expand All @@ -150,7 +137,7 @@ export default class HdsCodeBlock extends Component<HdsCodeBlockSignature> {

if (code) {
// eslint-disable-next-line ember/no-runloop
next(() => {
next((): void => {
if (language && grammar) {
this._prismCode = htmlSafe(Prism.highlight(code, grammar, language));
} else {
Expand All @@ -165,12 +152,20 @@ export default class HdsCodeBlock extends Component<HdsCodeBlockSignature> {
element,
});

// Get the actual height & the content height of the preCodeElement
// eslint-disable-next-line ember/no-runloop
schedule('afterRender', (): void => {
const preCodeElement = document.getElementById(this._preCodeId);
this._codeContentHeight = preCodeElement?.scrollHeight ?? 0;
this._codeContainerHeight = preCodeElement?.clientHeight ?? 0;
});

// Force prism-line-highlight plugin initialization
// Context: https://github.com/hashicorp/design-system/pull/1749#discussion_r1374288785
if (this.args.highlightLines) {
// we need to delay re-evaluating the context for prism-line-highlight for as much as possible, and `afterRender` is the 'latest' we can use in the component lifecycle
// eslint-disable-next-line ember/no-runloop
schedule('afterRender', () => {
schedule('afterRender', (): void => {
// we piggy-back on the plugin's `resize` event listener to trigger a new call of the `highlightLines` function: https://github.com/PrismJS/prism/blob/master/plugins/line-highlight/prism-line-highlight.js#L337
if (window) window.dispatchEvent(new Event('resize'));
});
Expand All @@ -179,11 +174,11 @@ export default class HdsCodeBlock extends Component<HdsCodeBlockSignature> {
}
}

/**
* Get the class names to apply to the component.
* @method classNames
* @return {string} The "class" attribute to apply to the component.
*/
@action
toggleExpanded(): void {
this._isExpanded = !this._isExpanded;
}

get classNames(): string {
// Currently there is only one theme so the class name is hard-coded.
// In the future, additional themes such as a "light" theme could be added.
Expand All @@ -206,6 +201,14 @@ export default class HdsCodeBlock extends Component<HdsCodeBlockSignature> {
classes.push('line-numbers');
}

if (this.showFooter) {
classes.push('hds-code-block--has-overlay-footer');
}

if (this._isExpanded) {
classes.push('hds-code-block--is-expanded');
}

return classes.join(' ');
}
}
107 changes: 96 additions & 11 deletions packages/components/src/styles/components/code-block/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
// DIMENSIONS
$hds-code-block-line-numbers-width: 49px; // 3em ≈ 49px
$hds-code-block-code-padding: 16px;
$hds-code-block-code-footer-height: 48px;

// CODE-BLOCK PARENT/WRAPPER

Expand All @@ -35,7 +36,6 @@ $hds-code-block-code-padding: 16px;
word-spacing: normal;
tab-size: 4;
hyphens: none;
scrollbar-width: thin;

@media print {
text-shadow: none;
Expand All @@ -47,13 +47,15 @@ $hds-code-block-code-padding: 16px;

// isStandalone
.hds-code-block--is-standalone {
overflow: hidden; // hide corners of scrollbar that poke out
border-radius: var(--token-border-radius-medium);
}

// hasLineWrapping
.hds-code-block--has-line-wrapping {
.hds-code-block__code,
.hds-code-block__code code {
display: block;
white-space: pre-wrap;
overflow-wrap: break-word;
}
Expand Down Expand Up @@ -128,10 +130,13 @@ $hds-code-block-code-padding: 16px;
// Code
.hds-code-block__code {
@include hds-focus-ring-basic();
display: block;
position: relative;
display: grid;
margin: 0;
padding: $hds-code-block-code-padding;
padding: $hds-code-block-code-padding 0 $hds-code-block-code-padding $hds-code-block-code-padding;
overflow: auto;
scrollbar-width: thin;
scrollbar-color: var(--token-color-palette-neutral-400) var(--token-color-palette-neutral-500);
font-size: 0.8125rem;
font-family: var(--token-typography-code-200-font-family);
border-radius: inherit;
Expand All @@ -140,32 +145,38 @@ $hds-code-block-code-padding: 16px;
color: var(--hds-code-block-color-foreground-selection);
background-color: var(--hds-code-block-color-surface-selection);
}

code {
display: inline-block;
padding-right: $hds-code-block-code-padding;
}
}

// CopyButton
// General dark button styles
.hds-code-block__height-toggle-button,
.hds-code-block__copy-button {
position: absolute;
top: 11px; // 12px -1px accounting for border
right: 12px; // 12px -1px accounting for border
// Overriding default colors
color: var(--hds-code-block-color-foreground-primary);
background-color: var(--hds-code-block-color-surface-faint);
border: 1px solid var(--hds-code-block-color-border-strong);

&:hover,
&.mock-hover {
color: var(--hds-code-block-color-foreground-primary);
background-color: var(--hds-code-block-color-surface-primary);
border-color: var(--hds-code-block-color-border-strong);
}

&:active,
&.mock-active {
color: var(--hds-code-block-color-foreground-primary);
background-color: var(--hds-code-block-color-surface-interactive-active);
}

&:focus,
&.mock-focus,
&:focus-visible {
color: var(--hds-code-block-color-foreground-primary);
background-color: var(--hds-code-block-color-surface-faint);
border-color: var(--hds-code-block-color-focus-action-internal);

Expand All @@ -174,6 +185,17 @@ $hds-code-block-code-padding: 16px;
}
}

.hds-icon {
color: var(--hds-code-block-color-foreground-primary);
}
}

// CopyButton
.hds-code-block__copy-button {
position: absolute;
top: 11px; // 12px -1px accounting for border
right: 12px;

&.hds-copy-button--status-success {
.hds-icon {
color: var(--hds-code-block-color-foreground-success);
Expand All @@ -185,12 +207,9 @@ $hds-code-block-code-padding: 16px;
color: var(--hds-code-block-color-foreground-critical);
}
}

.hds-icon {
color: var(--hds-code-block-color-foreground-primary);
}
}

// Prism.js plugins
.hds-code-block {
// LineNumbers plugin styles ---------------
// Note: Prism.js is using the specific class name "line-numbers" to determine implementation of line numbers in the UI
Expand All @@ -203,6 +222,11 @@ $hds-code-block-code-padding: 16px;
padding-left: calc(#{$hds-code-block-line-numbers-width} + #{$hds-code-block-code-padding});
}

.hds-code-block__overlay-footer {
// match horizontal padding of the code block
margin-left: $hds-code-block-line-numbers-width;
}

.line-numbers-rows {
position: absolute;
top: 0;
Expand Down Expand Up @@ -262,3 +286,64 @@ $hds-code-block-code-padding: 16px;
font-style: italic;
}
}

// Footer
.hds-code-block__overlay-footer {
position: absolute;
right: 0;
bottom: 0;
left: 0;
display: flex;
align-items: center;
justify-content: center;
padding: 10px $hds-code-block-code-padding 10px $hds-code-block-code-padding;
pointer-events: none; // fix issue with scrolling when hovering over the footer

// re-enable pointer events for the button (or any content inside the footer)
> * {
pointer-events: auto;
}
}

// Usage of sticky positioning, negative margins, and z-index are required to prevent styling issues
// when horizontal scrolling is present.
// https://hashicorp.slack.com/archives/C025N5V4PFZ/p1746659338984369
.hds-code-block--has-overlay-footer {
.hds-code-block__code::after {
// gradient element
position: sticky; // prevent gradient from scrolling together with content
bottom: -$hds-code-block-code-padding;
left: 0;
display: block;
height: $hds-code-block-code-footer-height;
margin: 0 0 -#{$hds-code-block-code-padding} -1000px; // cover line highlights when line numbers are enabled
background: linear-gradient(360deg, #0d0e12 37.07%, rgba(13, 14, 18, 25%) 100%);
content: "";
pointer-events: none;
}

.line-numbers .line-numbers-rows {
padding-bottom: $hds-code-block-code-footer-height;
}

// place line numbers on top of footer gradient
.line-numbers-rows {
z-index: 1;
isolation: isolate;
}
}

.hds-code-block--is-expanded {
.hds-code-block__code {
// account for the footer at the bottom of the code block
padding-bottom: $hds-code-block-code-footer-height;

&::after {
content: none;
}
}

.hds-code-block__overlay-footer {
background: none;
}
}
Loading