diff --git a/app/assets/javascripts/components/story/StoryCopyIdClipboard.js b/app/assets/javascripts/components/story/StoryCopyIdClipboard.js new file mode 100644 index 000000000..33425d484 --- /dev/null +++ b/app/assets/javascripts/components/story/StoryCopyIdClipboard.js @@ -0,0 +1,17 @@ +import React from "react"; +import Clipboard from "react-clipboard.js"; + +const StoryCopyIdClipboard = ({ id }) => { + return ( + + #{id} + + ); +}; + +export default StoryCopyIdClipboard; diff --git a/app/assets/javascripts/templates/story.ejs b/app/assets/javascripts/templates/story.ejs index 9accddcd7..552d0f34b 100644 --- a/app/assets/javascripts/templates/story.ejs +++ b/app/assets/javascripts/templates/story.ejs @@ -43,6 +43,7 @@
+
<% if (story.get('labels')) { %> <% _.each(story.labels(), function(value) { %> diff --git a/app/assets/javascripts/views/story_view.jsx b/app/assets/javascripts/views/story_view.jsx index a467487fb..9b90d2348 100644 --- a/app/assets/javascripts/views/story_view.jsx +++ b/app/assets/javascripts/views/story_view.jsx @@ -22,6 +22,7 @@ import storyTemplate from 'templates/story.ejs'; import alertTemplate from 'templates/alert.ejs'; import storyHoverTemplate from 'templates/story_hover.ejs'; import noteTemplate from 'templates/note.ejs'; +import StoryCopyIdClipboard from '../components/story/StoryCopyIdClipboard'; const LOCAL_STORY_REGEXP = /(?!\s|\b)(#\d+)(?!\w)/g; @@ -541,34 +542,28 @@ const StoryView = FormView.extend({ }, renderCollapsed: function(isGuest) { - this.$el.removeClass('editing'); this.$el.html(this.template({story: this.model, view: this})); - this.$el.toggleClass('collapsed-iteration', - !this.model.get('isVisible') && - !this.isSearchResult); + this.$el.toggleClass('collapsed-iteration', !this.model.get('isVisible') && !this.isSearchResult); const stateButtons = this.$('[data-story-state-buttons]').get(0) if(stateButtons) { - ReactDOM.render( - , - stateButtons - ); + ReactDOM.render(, stateButtons); } const estimateButtons = this.$('[data-story-estimate-buttons]').get(0) if(estimateButtons) { ReactDOM.render( - , + , estimateButtons ); } + const copyStoryIdClipboardLink = this.$('[data-story-id-copy-clipboard]').get(0) + if(copyStoryIdClipboardLink) { + ReactDOM.render(, copyStoryIdClipboardLink) + } + if (isGuest) { this.$el.find('.state-actions').find('.transition').prop('disabled', true) } }, diff --git a/app/assets/stylesheets/_screen.scss b/app/assets/stylesheets/_screen.scss index 9474a044b..b678168f6 100644 --- a/app/assets/stylesheets/_screen.scss +++ b/app/assets/stylesheets/_screen.scss @@ -224,10 +224,14 @@ div.story-controls { } div.story-title { - margin-left: 65px; + margin-left: 50px; word-wrap: break-word; } +div.story-title div[data-story-id-copy-clipboard] { + display: inline; +} + .unestimated div.story-title { font-style: italic; } @@ -239,6 +243,19 @@ div.story-title abbr.initials { border: none; } +div.story-title { + .story-id { + cursor: pointer; + color: $blue-4; + font-weight: bold; + border: none; + + &:hover { + text-decoration: underline; + } + } +} + .input-group-btn .btn-clipboard-id { padding-top: 6px; } diff --git a/spec/javascripts/components/story/story_copy_id_clipboard_spec.js b/spec/javascripts/components/story/story_copy_id_clipboard_spec.js new file mode 100644 index 000000000..27bde9a9e --- /dev/null +++ b/spec/javascripts/components/story/story_copy_id_clipboard_spec.js @@ -0,0 +1,36 @@ +import React from 'react'; +import { mount, shallow } from 'enzyme'; + +import StoryCopyIdClipboard from 'components/story/StoryCopyIdClipboard'; + +describe('', function() { + beforeEach(function() { + sinon.stub(I18n, 't'); + }); + + afterEach(function() { + I18n.t.restore(); + }); + + it("should render story id text", function() { + const wrapper = mount( + + ); + expect(wrapper.find('.story-id').at(0).text()).toBe('#70'); + }); + + it("should render story id data-clipboard-text", function() { + const wrapper = mount( + + ); + expect(wrapper.find('[data-clipboard-text]')).toExist(); + }); + + it("should render copy id title", function() { + shallow( + + ); + expect(I18n.t).toHaveBeenCalledWith('story.events.copy_id'); + }); + +});