diff --git a/changelog/unreleased/enhancement-textarea-configurable-enter-linebreak b/changelog/unreleased/enhancement-textarea-configurable-enter-linebreak new file mode 100644 index 000000000..1ecf5abbe --- /dev/null +++ b/changelog/unreleased/enhancement-textarea-configurable-enter-linebreak @@ -0,0 +1,7 @@ +Enhancement: OcTextarea configurable Enter/Linebreak + +OcTextArea has now an property 'submitOnEnter'. +This prop controls how the textarea should react to ENTER. + +https://github.com/owncloud/owncloud-design-system/issues/1422 +https://github.com/owncloud/owncloud-design-system/pull/1517 \ No newline at end of file diff --git a/src/components/OcTextarea.spec.js b/src/components/OcTextarea.spec.js new file mode 100644 index 000000000..c411b888f --- /dev/null +++ b/src/components/OcTextarea.spec.js @@ -0,0 +1,133 @@ +import { shallowMount } from "@vue/test-utils" +import OcTextarea from "./OcTextarea.vue" + +const defaultProps = { + label: "label", +} + +describe("OcTextarea", () => { + function getShallowWrapper(props = {}) { + return shallowMount(OcTextarea, { + propsData: { + ...defaultProps, + ...props, + }, + }) + } + + const selectors = { + textareaMessage: ".oc-textarea-message span", + textArea: ".oc-textarea", + } + describe("id prop", () => { + const wrapper = getShallowWrapper({ + id: "test-textarea-id", + descriptionMessage: "hello", + }) + it("should set provided id to the textarea", () => { + expect(wrapper.find("textarea").attributes().id).toBe("test-textarea-id") + }) + it("should set label target for provided id", () => { + expect(wrapper.find("label").attributes().for).toBe("test-textarea-id") + }) + it("should set message id according to provided id", () => { + expect(wrapper.find(selectors.textareaMessage).attributes().id).toBe( + "test-textarea-id-message" + ) + }) + }) + describe("label prop", () => { + it("should set provided label to the textarea", () => { + const wrapper = getShallowWrapper() + expect(wrapper.find("label").text()).toBe("label") + }) + }) + describe("when a description message is provided", () => { + const wrapper = getShallowWrapper({ descriptionMessage: "You should pass." }) + it("should add the description class to the textarea message", () => { + expect(wrapper.find(selectors.textareaMessage).attributes().class).toContain( + "oc-textarea-description" + ) + }) + it("should show the description message as the input message text", () => { + expect(wrapper.find(selectors.textareaMessage).text()).toBe("You should pass.") + }) + }) + describe("when a warning message is provided", () => { + const wrapper = getShallowWrapper({ warningMessage: "You may pass." }) + it("should add the warning class to the textarea", () => { + expect(wrapper.find("textarea").attributes().class).toContain("oc-textarea-warning") + }) + it("should add the warning class to the textarea message", () => { + expect(wrapper.find(selectors.textareaMessage).attributes().class).toContain( + "oc-textarea-warning" + ) + }) + it("should show the warning message as the textarea message text", () => { + expect(wrapper.find(selectors.textareaMessage).text()).toBe("You may pass.") + }) + }) + describe("when an error message is provided", () => { + const wrapper = getShallowWrapper({ errorMessage: "You shall not pass." }) + it("should add the error class to the textarea", () => { + expect(wrapper.find("textarea").attributes().class).toContain("oc-textarea-danger") + }) + it("should add the error class to the textarea message", () => { + expect(wrapper.find(selectors.textareaMessage).attributes().class).toContain( + "oc-textarea-danger" + ) + }) + it("should show the error message as the textarea message text", () => { + expect(wrapper.find(selectors.textareaMessage).text()).toBe("You shall not pass.") + }) + it("should set the input aria-invalid attribute to true", () => { + expect(wrapper.find("textarea").attributes("aria-invalid")).toBe("true") + }) + }) + describe("message priority", () => { + it("should give error message top priority", () => { + const wrapper = getShallowWrapper({ + errorMessage: "You shall not pass.", + warningMessage: "You may pass.", + descriptionMessage: "Your should pass.", + }) + const messageEl = wrapper.find(".oc-textarea-message span") + expect(messageEl.attributes().class).toBe( + "oc-textarea-description oc-textarea-warning oc-textarea-danger" + ) + expect(messageEl.text()).toBe("You shall not pass.") + }) + it("should give warning message priority over description message", () => { + const wrapper = getShallowWrapper({ + warningMessage: "You may pass.", + descriptionMessage: "Your should pass.", + }) + const messageEl = wrapper.find(selectors.textareaMessage) + expect(messageEl.attributes().class).toBe("oc-textarea-description oc-textarea-warning") + expect(messageEl.text()).toBe("You may pass.") + }) + }) + describe("input events", () => { + it("should emit an input event on typing", async () => { + const wrapper = getShallowWrapper() + expect(wrapper.emitted().input).toBeFalsy() + await wrapper.find("textarea").setValue("a") + expect(wrapper.emitted().input).toBeTruthy() + expect(wrapper.emitted().input[0][0]).toBe("a") + }) + }) + describe("change events", () => { + it("should emit a change event if submitOnEnter is true", async () => { + const wrapper = getShallowWrapper({ submitOnEnter: true }) + expect(wrapper.emitted().change).toBeFalsy() + await wrapper.find("textarea").trigger("keydown.enter") + expect(wrapper.emitted().change).toBeTruthy() + }) + it("shouldn't emit a change event if submitOnEnter is false", async () => { + const wrapper = getShallowWrapper({ submitOnEnter: false }) + expect(wrapper.emitted().change).toBeFalsy() + await wrapper.find("textarea").trigger("keydown.enter") + expect(wrapper.emitted().change).toBeFalsy() + }) + }) +}) diff --git a/src/components/OcTextarea.vue b/src/components/OcTextarea.vue index 6a51eed51..87f232f80 100644 --- a/src/components/OcTextarea.vue +++ b/src/components/OcTextarea.vue @@ -102,6 +102,16 @@ export default { type: Boolean, default: false, }, + /** + * Configure if the value should be emitted on 'enter' or if it should do a linebreak + * if true: 'enter' emits value, ctrl + enter and shift + enter creates linebreak + * if false: 'enter' creates linebreak + */ + submitOnEnter: { + type: Boolean, + required: false, + default: true, + }, }, computed: { showMessageLine() { @@ -158,9 +168,11 @@ export default { this.$emit("focus", value) }, onKeyDown(e) { - if (e.keyCode === 13) { + const enterKey = e.key === "Enter" + if (this.submitOnEnter && enterKey && !e.ctrlKey && !e.shiftKey) { /** - * Change event - emitted as soon as the user hits enter + * Change event - emitted as soon as the user hits enter (without ctrl or shift) + * Only applies if submitOnEnter is set to true * @type {string} */ this.$emit("change", e.target.value)