Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[stable28] Feat/4750 Make code type fields copyable #5403

Merged
merged 6 commits into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
23 changes: 18 additions & 5 deletions cypress/e2e/nodes/CodeBlock.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,21 @@ describe('Front matter support', function() {
cy.getContent().find('code').eq(1).find('.hljs-string').eq(0).contains('"bar"')
cy.getContent().find('code').eq(1).find('.hljs-keyword').eq(1).contains('function')

cy.getContent().find('.code-block').eq(1).find('[data-cy="copy-code"]').click()

// Copy code to clipboard
cy.getContent().find('code').eq(1).invoke('text')
.then(code =>
cy.window().then(win => {
win.navigator.clipboard.readText()
.then(copiedCode => {
expect(copiedCode).to.equal(code)
})
}),
)

// Remove language
cy.getContent().find('.code-block').eq(1).find('.view-switch button').click()
cy.getContent().find('.code-block').eq(1).find('[data-cy="code-action-group"]').find('div:first-child').click()
// FIXME: Label behaviour changed, should be back once https://github.com/nextcloud-libraries/nextcloud-vue/pull/4484 is merged
// cy.get('.action-input__text-label').contains('Code block language')
cy.get('.input-field__input:visible').clear()
Expand All @@ -53,7 +66,7 @@ describe('Front matter support', function() {
cy.getContent().find('code').eq(1).find('.hljs-keyword').should('not.exist')

// Re-add language
cy.getContent().find('.code-block').eq(1).find('.view-switch button').click()
cy.getContent().find('.code-block').eq(1).find('[data-cy="code-action-group"]').find('div:first-child').click()
cy.get('.input-field__input:visible').type('javascript')

cy.getContent().find('code').eq(1).find('.hljs-keyword').eq(0).contains('const')
Expand Down Expand Up @@ -136,17 +149,17 @@ describe('Front matter support', function() {
cy.get('.split-view__code').find('code').should('be.visible')
cy.get('.split-view__preview').find('svg').should('be.visible')

cy.getContent().find('.code-block').eq(0).find('.view-switch button').click()
cy.getContent().find('.code-block').eq(0).find('.view-switch div div button').click()
cy.get('.action-button').eq(0).contains('Source code').click()
cy.get('.split-view__code').find('code').should('be.visible')
cy.get('.split-view__preview').find('svg').should('not.be.visible')

cy.getContent().find('.code-block').eq(0).find('.view-switch button').click()
cy.getContent().find('.code-block').eq(0).find('.view-switch div div button').click()
cy.get('.action-button').eq(1).contains('Diagram').click()
cy.get('.split-view__code').find('code').should('not.be.visible')
cy.get('.split-view__preview').find('svg').should('be.visible')

cy.getContent().find('.code-block').eq(0).find('.view-switch button').click()
cy.getContent().find('.code-block').eq(0).find('.view-switch div div button').click()
cy.get('.action-button').eq(2).contains('Both').click()
cy.get('.split-view__code').find('code').should('be.visible')
cy.get('.split-view__preview').find('svg').should('be.visible')
Expand Down
41 changes: 41 additions & 0 deletions src/mixins/CopyToClipboardMixin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { showError, showSuccess } from '@nextcloud/dialogs'

export default {
data() {
return {
copied: false,
copyLoading: false,
copySuccess: false,
}
},

methods: {
async copyToClipboard(content) {
// change to loading status
this.copyLoading = true

// copy link to clipboard
try {
await navigator.clipboard.writeText(content)
this.copySuccess = true
this.copied = true

// Notify success
showSuccess(t('text', 'Copied to the clipboard'))
} catch (error) {
this.copySuccess = false
this.copied = true
showError(
`<div>${t('text', 'Could not copy to the clipboard')}</div>`,
{ isHTML: true })
} finally {
this.copyLoading = false
setTimeout(() => {
// stop loading status regardless of outcome
this.copySuccess = false
this.copied = false
}, 4000)
}
},
},
}
37 changes: 34 additions & 3 deletions src/nodes/CodeBlockView.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,24 @@
<template>
<NodeViewWrapper as="div" :data-mode="viewMode" class="code-block">
<div v-if="isEditable" class="code-block-header">
<div class="code-block-header">
<div class="view-switch">
<NcActions :aria-label="t('text', 'Code block options')">
<NcActions :aria-label="t('text', 'Copy code block')">
<NcActionButton v-if="hasCode"
data-cy="copy-code"
:aria-label="t('text', 'Copy code')"
:close-after-click="false"
@click="copyCode">
<template #icon>
<Check v-if="copySuccess" :size="20" />
<NcLoadingIcon v-else-if="copyLoading" :size="20" />
<ContentCopy v-else :size="20" />
</template>
</NcActionButton>
</NcActions>

<NcActions v-if="isEditable"
data-cy="code-action-group"
:aria-label="t('text', 'Code block options')">
<NcActionInput :label="t('text', 'Code block language')"
:value="type"
:show-trailing-button="false"
Expand Down Expand Up @@ -59,7 +75,7 @@
<script>
import debounce from 'debounce'
import { NodeViewWrapper, NodeViewContent } from '@tiptap/vue-2'
import { NcActions, NcActionButton, NcActionInput, NcActionLink, NcActionSeparator } from '@nextcloud/vue'
import { NcActions, NcActionButton, NcActionInput, NcActionLink, NcActionSeparator, NcLoadingIcon } from '@nextcloud/vue'
import mermaid from 'mermaid'
import { v4 as uuidv4 } from 'uuid'

Expand All @@ -68,13 +84,19 @@ import CodeBraces from 'vue-material-design-icons/CodeBraces.vue'
import Eye from 'vue-material-design-icons/Eye.vue'
import MarkerIcon from 'vue-material-design-icons/Marker.vue'
import Help from 'vue-material-design-icons/Help.vue'
import ContentCopy from 'vue-material-design-icons/ContentCopy.vue'
import Check from 'vue-material-design-icons/Check.vue'

import CopyToClipboardMixin from '../mixins/CopyToClipboardMixin.js'

export default {
// eslint-disable-next-line vue/match-component-file-name
name: 'CodeBlockView',
components: {
MarkerIcon,
ContentCopy,
Help,
Check,
Eye,
ViewSplitVertical,
CodeBraces,
Expand All @@ -83,9 +105,11 @@ export default {
NcActionInput,
NcActionLink,
NcActionSeparator,
NcLoadingIcon,
NodeViewWrapper,
NodeViewContent,
},
mixins: [CopyToClipboardMixin],
props: {
node: {
type: Object,
Expand All @@ -104,6 +128,9 @@ export default {
}
},
computed: {
hasCode() {
return this.node?.textContent
},
type() {
return this.node?.attrs?.language || ''
},
Expand Down Expand Up @@ -169,6 +196,9 @@ export default {
})
},
methods: {
async copyCode() {
await this.copyToClipboard(this.node?.textContent)
},
updateLanguage(event) {
this.updateAttributes({
language: event.target.value,
Expand All @@ -180,6 +210,7 @@ export default {
},
}
</script>

<style lang="scss" scoped>
.code-block {
background-color: var(--color-background-dark);
Expand Down
Loading