mirror of
https://github.com/twbs/bootstrap.git
synced 2025-02-23 00:39:54 +00:00
Add js-docs shortcode to ensure consistency between doc and js code (#38316)
Co-authored-by: XhmikosR <xhmikosr@gmail.com>
This commit is contained in:
parent
31f93e2aff
commit
06f7c3b6c7
@ -16,8 +16,6 @@ const uriAttributes = new Set([
|
|||||||
'xlink:href'
|
'xlink:href'
|
||||||
])
|
])
|
||||||
|
|
||||||
const ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A pattern that recognizes a commonly useful subset of URLs that are safe.
|
* A pattern that recognizes a commonly useful subset of URLs that are safe.
|
||||||
*
|
*
|
||||||
@ -48,6 +46,9 @@ const allowedAttribute = (attribute, allowedAttributeList) => {
|
|||||||
.some(regex => regex.test(attributeName))
|
.some(regex => regex.test(attributeName))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// js-docs-start allow-list
|
||||||
|
const ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i
|
||||||
|
|
||||||
export const DefaultAllowlist = {
|
export const DefaultAllowlist = {
|
||||||
// Global attributes allowed on any supplied element below.
|
// Global attributes allowed on any supplied element below.
|
||||||
'*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],
|
'*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],
|
||||||
@ -81,6 +82,7 @@ export const DefaultAllowlist = {
|
|||||||
u: [],
|
u: [],
|
||||||
ul: []
|
ul: []
|
||||||
}
|
}
|
||||||
|
// js-docs-end allow-list
|
||||||
|
|
||||||
export function sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) {
|
export function sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) {
|
||||||
if (!unsafeHtml.length) {
|
if (!unsafeHtml.length) {
|
||||||
|
@ -60,6 +60,7 @@
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Instantiate all toasts in docs pages only
|
// Instantiate all toasts in docs pages only
|
||||||
|
// js-docs-start live-toast
|
||||||
const toastTrigger = document.getElementById('liveToastBtn')
|
const toastTrigger = document.getElementById('liveToastBtn')
|
||||||
const toastLiveExample = document.getElementById('liveToast')
|
const toastLiveExample = document.getElementById('liveToast')
|
||||||
|
|
||||||
@ -69,14 +70,15 @@
|
|||||||
toastBootstrap.show()
|
toastBootstrap.show()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
// js-docs-end live-toast
|
||||||
|
|
||||||
// -------------------------------
|
// -------------------------------
|
||||||
// Alerts
|
// Alerts
|
||||||
// -------------------------------
|
// -------------------------------
|
||||||
// Used in 'Show live toast' example in docs or StackBlitz
|
// Used in 'Show live alert' example in docs or StackBlitz
|
||||||
const alertPlaceholder = document.getElementById('liveAlertPlaceholder')
|
|
||||||
const alertTrigger = document.getElementById('liveAlertBtn')
|
|
||||||
|
|
||||||
|
// js-docs-start live-alert
|
||||||
|
const alertPlaceholder = document.getElementById('liveAlertPlaceholder')
|
||||||
const appendAlert = (message, type) => {
|
const appendAlert = (message, type) => {
|
||||||
const wrapper = document.createElement('div')
|
const wrapper = document.createElement('div')
|
||||||
wrapper.innerHTML = [
|
wrapper.innerHTML = [
|
||||||
@ -89,11 +91,13 @@
|
|||||||
alertPlaceholder.append(wrapper)
|
alertPlaceholder.append(wrapper)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const alertTrigger = document.getElementById('liveAlertBtn')
|
||||||
if (alertTrigger) {
|
if (alertTrigger) {
|
||||||
alertTrigger.addEventListener('click', () => {
|
alertTrigger.addEventListener('click', () => {
|
||||||
appendAlert('Nice, you triggered this alert message!', 'success')
|
appendAlert('Nice, you triggered this alert message!', 'success')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
// js-docs-end live-alert
|
||||||
|
|
||||||
// --------
|
// --------
|
||||||
// Carousels
|
// Carousels
|
||||||
@ -130,6 +134,7 @@
|
|||||||
// Modal
|
// Modal
|
||||||
// -------------------------------
|
// -------------------------------
|
||||||
// Modal 'Varying modal content' example in docs and StackBlitz
|
// Modal 'Varying modal content' example in docs and StackBlitz
|
||||||
|
// js-docs-start varying-modal-content
|
||||||
const exampleModal = document.getElementById('exampleModal')
|
const exampleModal = document.getElementById('exampleModal')
|
||||||
if (exampleModal) {
|
if (exampleModal) {
|
||||||
exampleModal.addEventListener('show.bs.modal', event => {
|
exampleModal.addEventListener('show.bs.modal', event => {
|
||||||
@ -137,6 +142,8 @@
|
|||||||
const button = event.relatedTarget
|
const button = event.relatedTarget
|
||||||
// Extract info from data-bs-* attributes
|
// Extract info from data-bs-* attributes
|
||||||
const recipient = button.getAttribute('data-bs-whatever')
|
const recipient = button.getAttribute('data-bs-whatever')
|
||||||
|
// If necessary, you could initiate an Ajax request here
|
||||||
|
// and then do the updating in a callback.
|
||||||
|
|
||||||
// Update the modal's content.
|
// Update the modal's content.
|
||||||
const modalTitle = exampleModal.querySelector('.modal-title')
|
const modalTitle = exampleModal.querySelector('.modal-title')
|
||||||
@ -146,6 +153,7 @@
|
|||||||
modalBodyInput.value = recipient
|
modalBodyInput.value = recipient
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
// js-docs-end varying-modal-content
|
||||||
|
|
||||||
// -------------------------------
|
// -------------------------------
|
||||||
// Offcanvas
|
// Offcanvas
|
||||||
|
@ -38,28 +38,7 @@ Click the button below to show an alert (hidden with inline styles to start), th
|
|||||||
|
|
||||||
We use the following JavaScript to trigger our live alert demo:
|
We use the following JavaScript to trigger our live alert demo:
|
||||||
|
|
||||||
```js
|
{{< js-docs name="live-alert" file="site/assets/js/snippets.js" >}}
|
||||||
const alertPlaceholder = document.getElementById('liveAlertPlaceholder')
|
|
||||||
|
|
||||||
const alert = (message, type) => {
|
|
||||||
const wrapper = document.createElement('div')
|
|
||||||
wrapper.innerHTML = [
|
|
||||||
`<div class="alert alert-${type} alert-dismissible" role="alert">`,
|
|
||||||
` <div>${message}</div>`,
|
|
||||||
' <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>',
|
|
||||||
'</div>'
|
|
||||||
].join('')
|
|
||||||
|
|
||||||
alertPlaceholder.append(wrapper)
|
|
||||||
}
|
|
||||||
|
|
||||||
const alertTrigger = document.getElementById('liveAlertBtn')
|
|
||||||
if (alertTrigger) {
|
|
||||||
alertTrigger.addEventListener('click', () => {
|
|
||||||
alert('Nice, you triggered this alert message!', 'success')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Link color
|
### Link color
|
||||||
|
|
||||||
|
@ -481,24 +481,7 @@ Below is a live demo followed by example HTML and JavaScript. For more informati
|
|||||||
</div>
|
</div>
|
||||||
{{< /example >}}
|
{{< /example >}}
|
||||||
|
|
||||||
```js
|
{{< js-docs name="varying-modal-content" file="site/assets/js/snippets.js" >}}
|
||||||
const exampleModal = document.getElementById('exampleModal')
|
|
||||||
exampleModal.addEventListener('show.bs.modal', event => {
|
|
||||||
// Button that triggered the modal
|
|
||||||
const button = event.relatedTarget
|
|
||||||
// Extract info from data-bs-* attributes
|
|
||||||
const recipient = button.getAttribute('data-bs-whatever')
|
|
||||||
// If necessary, you could initiate an Ajax request here
|
|
||||||
// and then do the updating in a callback.
|
|
||||||
//
|
|
||||||
// Update the modal's content.
|
|
||||||
const modalTitle = exampleModal.querySelector('.modal-title')
|
|
||||||
const modalBodyInput = exampleModal.querySelector('.modal-body input')
|
|
||||||
|
|
||||||
modalTitle.textContent = `New message to ${recipient}`
|
|
||||||
modalBodyInput.value = recipient
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Toggle between modals
|
### Toggle between modals
|
||||||
|
|
||||||
|
@ -87,17 +87,7 @@ Click the button below to show a toast (positioned with our utilities in the low
|
|||||||
|
|
||||||
We use the following JavaScript to trigger our live toast demo:
|
We use the following JavaScript to trigger our live toast demo:
|
||||||
|
|
||||||
```js
|
{{< js-docs name="live-toast" file="site/assets/js/snippets.js" >}}
|
||||||
const toastTrigger = document.getElementById('liveToastBtn')
|
|
||||||
const toastLiveExample = document.getElementById('liveToast')
|
|
||||||
|
|
||||||
if (toastTrigger) {
|
|
||||||
const toastBootstrap = bootstrap.Toast.getOrCreateInstance(toastLiveExample)
|
|
||||||
toastTrigger.addEventListener('click', () => {
|
|
||||||
toastBootstrap.show()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Translucent
|
### Translucent
|
||||||
|
|
||||||
|
@ -231,42 +231,7 @@ Tooltips and Popovers use our built-in sanitizer to sanitize options which accep
|
|||||||
|
|
||||||
The default `allowList` value is the following:
|
The default `allowList` value is the following:
|
||||||
|
|
||||||
```js
|
{{< js-docs name="allow-list" file="js/src/util/sanitizer.js" >}}
|
||||||
const ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i
|
|
||||||
const DefaultAllowlist = {
|
|
||||||
// Global attributes allowed on any supplied element below.
|
|
||||||
'*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],
|
|
||||||
a: ['target', 'href', 'title', 'rel'],
|
|
||||||
area: [],
|
|
||||||
b: [],
|
|
||||||
br: [],
|
|
||||||
col: [],
|
|
||||||
code: [],
|
|
||||||
div: [],
|
|
||||||
em: [],
|
|
||||||
hr: [],
|
|
||||||
h1: [],
|
|
||||||
h2: [],
|
|
||||||
h3: [],
|
|
||||||
h4: [],
|
|
||||||
h5: [],
|
|
||||||
h6: [],
|
|
||||||
i: [],
|
|
||||||
img: ['src', 'srcset', 'alt', 'title', 'width', 'height'],
|
|
||||||
li: [],
|
|
||||||
ol: [],
|
|
||||||
p: [],
|
|
||||||
pre: [],
|
|
||||||
s: [],
|
|
||||||
small: [],
|
|
||||||
span: [],
|
|
||||||
sub: [],
|
|
||||||
sup: [],
|
|
||||||
strong: [],
|
|
||||||
u: [],
|
|
||||||
ul: []
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
If you want to add new values to this default `allowList` you can do the following:
|
If you want to add new values to this default `allowList` you can do the following:
|
||||||
|
|
||||||
|
37
site/layouts/shortcodes/js-docs.html
Normal file
37
site/layouts/shortcodes/js-docs.html
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
{{- /*
|
||||||
|
Usage: `js-docs name="name" file="file/_location.js`
|
||||||
|
|
||||||
|
Prints everything between `// js-docs-start "name"` and `// js-docs-end "name"`
|
||||||
|
comments in the docs.
|
||||||
|
*/ -}}
|
||||||
|
|
||||||
|
{{- $name := .Get "name" -}}
|
||||||
|
{{- $file := .Get "file" -}}
|
||||||
|
|
||||||
|
{{- /* If any parameters are missing, print an error and exit */ -}}
|
||||||
|
{{- if or (not $name) (not $file) -}}
|
||||||
|
{{- errorf "%s: %q: Missing required parameters! Got: name=%q file=%q!" .Position .Name $name $file -}}
|
||||||
|
{{- else -}}
|
||||||
|
{{- $capture_start := printf "// js-docs-start %s\n" $name -}}
|
||||||
|
{{- $capture_end := printf "// js-docs-end %s" $name -}}
|
||||||
|
{{- $regex := printf `%s((?:.|\n)*)%s` $capture_start $capture_end -}}
|
||||||
|
|
||||||
|
{{- $match := findRE $regex (readFile $file) -}}
|
||||||
|
{{- $match = index $match 0 -}}
|
||||||
|
|
||||||
|
{{- if not $match -}}
|
||||||
|
{{- errorf "%s: %q: Got no matches for name=%q in file=%q!" .Position .Name $name $file -}}
|
||||||
|
{{- end -}}
|
||||||
|
|
||||||
|
{{- $match = replace $match $capture_start "" -}}
|
||||||
|
{{- $match = replace $match $capture_end "" -}}
|
||||||
|
|
||||||
|
<div class="bd-example-snippet bd-code-snippet">
|
||||||
|
<div class="bd-clipboard">
|
||||||
|
<button type="button" class="btn-clipboard" title="Copy to clipboard">
|
||||||
|
<svg class="bi" aria-hidden="true"><use xlink:href="#clipboard"></use></svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{{- highlight $match "js" "" -}}
|
||||||
|
</div>
|
||||||
|
{{- end -}}
|
Loading…
x
Reference in New Issue
Block a user